Using the Camera in React Native

In the last few articles, we have been working with React Native and have learned how to use some of React Native’s built in component. Most recently, we learned how to navigate between different screens using React Navigation.

One thing we haven’t covered yet, is getting access to the camera and camera roll in a React Native app. Now a days, it seems like every app has access to the phone’s camera. It is used to take photos, to scan QR codes, augmented reality and much more. A lot of these apps can also access the phone’s camera roll to either save photos or allow a user to select a photo from the camera roll. Therefore, in this article, we will be learning how to gain access to the camera and camera roll.

Getting Started

I will be working on a Mac, using Visual Studio Code as my editor and will run the app on the iOS simulator. If you are using Windows or are targeting Android, I will test the app on the Android emulator at the end of the article.

If you are working with Expo, we will be creating a different project after completing the React Native project.

Let’s begin by creating a new React Native project. I will be calling this project, RNCamera. Run the following code in the Terminal.

react-native init RNCamera

Now that we have our project created, let’s go create a src folder to hold our screens and components folders. Here is how our project will be structured.

Once you have the folders created, create a new file called Main.js in the screens folder. Then we need to make changes to the App.js file. Here is the code for App.js and Main.js

App.js

import React, { Component } from 'react';
import Main from './src/screens/Main';
class App extends Component {
render() {
return <Main />
}
}
export default App;

Main.js

import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1
}
})
class Main extends Component {
render() {
return (
<View style={styles.container} />
)
}
}
export default Main;

The plan here will be to have a one-page application consisting of two parts. The first part is going to be an image component. The second component will be a button that when pressed, will allow the user to either take or choose an image from their phone.

Let’s first start with the image component. Create a new file called PhotoComponent.js, inside of the components folder. Then import this new file in Main.js, it will look like this.

Main.js

import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import PhotoComponent from '../components/PhotoComponent'
const styles = StyleSheet.create({
container: {
flex: 1
}
})
class Main extends Component {
render() {
return (
<View style={styles.container}>
<PhotoComponent />
</View>
)
}
}
export default Main;

Now, in PhotoComponent.js, let’s use React Native’s Image component to display an image of a camera. I downloaded two images and stored them inside of a new folder I created, called resources. The first image is one of a hexagon, which I will use as a background, and the second is that of a camera, which will be on top of the hexagon.

Here is the code for the PhotoComponent.js file.

import React, { Component } from 'react';
import { Dimensions, Image, StyleSheet, View } from 'react-native';
const width = Dimensions.get('window').width;
const largeContainerSize = width / 2;
const largeImageSize = width / 4;
const styles = StyleSheet.create({
container: {
flex: 3,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 10
},
containerSize: {
width: largeContainerSize,
height: largeContainerSize,
alignItems: 'center',
justifyContent: 'center',
tintColor: 'grey'
},
imageSize: {
width: largeImageSize,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
},
})
class PhotoComponent extends Component {
render() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.containerSize}
source={require('../resources/background.png')}
/>
<Image
resizeMode='contain'
style={styles.imageSize}
source={require('../resources/camera.png')}
/>
</View>
)
}
}
export default PhotoComponent;

The first two lines are imports we will be using from React and React Native.

The one import that we haven’t used before is Dimensions. This will allow us to get the dimensions of the device the app is running on, both height and width. We will use Dimensions to size our images dynamically based on the user’s screen size.

The next couple lines are constants that will be used to size the images. The first one gets the width of the screen. The next line, const largeContainerSize, is set to half the width of the screen and it will be used for the background image. The next one, largeImageSize, is set to a quarter of the screen’s width.

Then we have our styling. Our container has a flex value of 3 because I want this component to take up most of the screen. In the containerSize, which is the styling for the background image, we give it a tintColor of grey. This changes the color of the original image. And finally, in imageSize, which is the styling for the camera image, we give it a position of absolute because we want it to lay on top of the background image. The other properties that I didn’t mention, are to used to center the images, give it some padding and give it a specific size.

Then we have the class. Here we are returning a View with two Images. The first image is the background image and the second is the camera image.

Now save the files and run the app using the following command.

react-native run-ios

Depending on the images you chose, you may have something like this.

Great! Time to add a button.

Begin by creating a button component called, ButtonComponent.js in the components folder. Then import it in Main.js and add it in the render function, below the PhotoComponent.

Our button will be using an icon, which we will get from a third party library. We will be using react-native-vector-icons and to do so we must first install it, then link it.

To install react-native-vector-icons, run the following command while inside of your project directory.

npm install --save react-native-vector-icons

Once installed, run the following command to link it.

react-native link react-native-vector-icons

With that out the way, let’s work on ButtonComponent.js file. We will import from React and React Native. Import Icon from react-native-vector-icons. Then comes the styling and the class. The class consist of a TouchableOpacity, Icon and View components. The View will be used to create a round gray background for the button. Here is the code.

ButtonComponent.js

import React, { Component } from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
const styles = StyleSheet.create({
buttonContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
buttonBorder: {
borderColor: 'grey',
borderWidth: 1,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 35,
width: 70,
height: 70,
backgroundColor: 'grey'
},
})
class ButtonComponent extends Component {
render() {
return (
<TouchableOpacity style={styles.buttonContainer}>
<View style={styles.buttonBorder}>
<Icon
name='plus'
size={35}
color='white'/>
</View>
</TouchableOpacity>
)
}
}
export default ButtonComponent;

Save the files and reload the app. If you come upon any errors, close the Metro Bundler and run the project again.

Button looks good. We used the plus icon from FontAwesome and if you want to use different icon, go to https://fontawesome.com/icons?d=gallery to check out their options.

Time to gain access to the camera through React Native. We will be installing react-native-image-picker, which is, “A React Native module that allows you to use native UI to select a photo/video from the device library or directly from the camera.” You can learn more about it at https://github.com/react-native-community/react-native-image-picker.

Begin by installing react-native-image-picker. Use the following command in the Terminal.

npm install --save react-native-image-picker

Once installed, link it by using the following command.

react-native link react-native-image-picker

Now that it is linked, we need to go into the Android and iOS native code to ask the user for permission to take photos or to use an image from their camera roll.

Let’s begin with iOS. Inside of the iOS folder, open the RNCamera folder and open the info.plist file. In this file add the following between the <dict> tags.

<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) would like access to your photo gallery</string>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) would like to use your camera</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>$(PRODUCT_NAME) would like to save photos to your photo gallery</string>

This code will ask iOS users for permission. Time to do the same for Android users. Head to the Android folder and the AndroidManifest.xml file will be under app/src/main. In it add the following code, which can be added below the code asking for user permission to access the internet at the top of the file.

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

To learn more about the setup, please visit https://github.com/react-native-community/react-native-image-picker/blob/master/docs/Install.md.

With react-native-image-picker installed and the permission code added, we can now add it to our Main.js file.

We will begin by importing react-native-image-picker, using constructor and creating a state, creating a function for the image picker and passing the onPress prop to ButtonComponent. Here is the code.

Main.js

import ImagePicker from "react-native-image-picker";
const styles = StyleSheet.create({
container: {
flex: 1
}
})
class Main extends Component {
constructor(props) {
super(props)
this.state = {
uploadSource: null
}
}
selectPhotoTapped() {
const options = {
quality: 1.0,
maxWidth: 200,
maxHeight: 200,
storageOptions: {
skipBackup: true
}
};
ImagePicker.showImagePicker(options, response => {
console.log("Response = ", response);
if (response.didCancel) {
console.log("User cancelled photo picker");
} else if (response.error) {
console.log("ImagePicker Error: ", response.error);
} else {
let source = { uri: response.uri };
this.setState({
uploadSource: source
});
}
});
}
render() {
return (
<View style={styles.container}>
<PhotoComponent />
<ButtonComponent onPress={this.selectPhotoTapped.bind(this)}/>
</View>
)
}
}

The selectPhotoTapped() function, starts with a constant, option, which sets the max width and max height of the image. Next, we have ImagePicker.showImagePicker, which opens the image picker and returns console logs if the user cancels it or there is an error. If they choose or take a picture, then the state is updated to have upLoadSource equal to the source of the image. Then this function is passed as a prop to ButtonComponent, so that the TouchableOpacity button has access to the function.

Now go to ButtonComponent.js and pass the onPress prop to the TouchableOpacity component. Also, since this component does not use state or lifecycle functions, we can make a stateless function.

ButtonComponent.js

import React, { Component } from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
const styles = StyleSheet.create({
buttonContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
buttonBorder: {
borderColor: 'grey',
borderWidth: 1,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 35,
width: 70,
height: 70,
backgroundColor: 'grey'
},
})
const ButtonComponent = ({ onPress }) => (
<TouchableOpacity onPress={onPress} style={styles.buttonContainer}>
<View style={styles.buttonBorder}>
<Icon
name='plus'
size={35}
color='white'/>
</View>
</TouchableOpacity>
)
export default ButtonComponent;

Save the files and reload the app. If you run into any issues, try closing the Metro Bundler and run the react-native run-ios command again.

Great! The option to take a photo or choose one from the library appears. But if we pick an image from the library will it work? Let’s try it. Press the Choose from Library button and this will happen.

That’s a good sign. It shows us that the permission code we used worked. Let’s allow it and continue. Here is the next screen.

I’m going to pick the first photo in the Camera Roll folder.

Wait, nothing happened. This is because we are not passing upLoadSource to the PhotoComponent. Before we continue, let’s make sure that upLoadSource has something set to it. To check that upLoadSoucre has a value set to it, we will use console log. Add this line of code in the selectPhotoTapped function, right after setting the state.

Main.js

} else {
let source = { uri: response.uri };
this.setState({
uploadSource: source
);
console.log(this.state.uploadSource)
}

Save the file. Then in the simulator, press both the Command and D buttons to option up the React Native Development options. If you are using the Android emulator on a Mac, press Command and M. If you are using the Android emulator on a Windows computer, press Control and M. Then select Debug JS Remotely, and this will open up a tab in Google Chrome with the URL http://localhost:8081/debugger-ui. If you do not have Google Chrome, please download it or head over to, https://facebook.github.io/react-native/docs/debugging, for other options.

Once the Google Chrome tab opens up, select View from the top menu and then select Developer/Developer Tools. With the debugger now running, reload the app and select an image from the camera roll and see what is displayed in the console.

Awesome! We see that our upLoadSource state has the url of the image. We also see the other console log I added which was meant to show more information about the image is displaying too. The other console logs are meant to show only if there are errors.

Now we should pass upLoadSource to our PhotoComponent. You can stop debugging remotely for now by pressing Command and D, Command and M, or Control and M, then selecting Stop Remote JS Debugging.

Pass the state of uploadSource to the PhotoComponent.

Main.js

<View style={styles.container}>
<PhotoComponent uri={this.state.uploadSource} />
<ButtonComponent onPress={this.selectPhotoTapped.bind(this)}/>
</View>

Then in PhotoComponent, we will check to whether we have a source for an image. To do this we will use the conditional operator “?”.

PhotoComponent.js

renderDefault() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.containerSize}
source={require('../resources/background.png')}
/>
<Image
resizeMode='contain'
style={styles.imageSize}
source={require('../resources/camera.png')}
/>
</View>
)
}
renderImage() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.imageSize}
source={this.props.uri}/>
</View>
)
}
render() {
const displayImage = this.props.uri ? this.renderImage() : this.renderDefault()
return (
<View style={styles.container}>
{displayImage}
</View>
)
}

Inside of the render() function we create a variable named displayImage and it is equal to a conditional operator. If this.props.uri is not null and has a value, then the renderImage() function is called, else the renderDefault() function is called. This variable, displayImage, replaces the code we had between the View tags in the render() function, which was the background image and the camera image. The background image and camera image, are placed in the renderDefault() function. The renderImage() function is where our chosen image will render.

Save the files and reload the app then add a photo from the phone’s camera roll.

Ok, not perfect but the image I chose did display. Let’s make a new set of styles to make this image a bit bigger.

PhotoComponent.js

chosenImage: {
width: width / 1.25,
height: width / 1.25,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
}
renderImage() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.chosenImage}
source={this.props.uri}/>
</View>
)
}

The styling is very similar to the camera image, but we are dividing by 1.25 instead of 4, which will make our chosen image much bigger.

Save the files, reload the app and try it again.

That’s much better! The image looks great and we can replace it by pressing on the plus button and choosing another image.

I think it a good time to test this code on Android. Begin by opening the Android emulator, then run the following command.

react-native run-android

It seems like the Android emulator does not have any photos in the camera roll, but you are able to take a photo. This is the result of taking a photo.

Great! It works for Android too. And if you try to select an image from the camera roll, you will see that the image we took is saved there.

Before we get into Expo, here is the code for RNCamera project we created.

Main.js

import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import PhotoComponent from '../components/PhotoComponent';
import ButtonComponent from '../components/ButtonComponent';
import ImagePicker from "react-native-image-picker";
const styles = StyleSheet.create({
container: {
flex: 1
}
})
class Main extends Component {
constructor(props) {
super(props)
this.state = {
uploadSource: null
}
}
selectPhotoTapped() {
const options = {
quality: 1.0,
maxWidth: 200,
maxHeight: 200,
storageOptions: {
skipBackup: true
}
};
ImagePicker.showImagePicker(options, response => {
console.log("Response = ", response);
if (response.didCancel) {
console.log("User cancelled photo picker");
} else if (response.error) {
console.log("ImagePicker Error: ", response.error);
} else {
let source = { uri: response.uri };
this.setState({
uploadSource: source
});
console.log(this.state.uploadSource)
}
});
}
render() {
return (
<View style={styles.container}>
<PhotoComponent uri={this.state.uploadSource} />
<ButtonComponent onPress={this.selectPhotoTapped.bind(this)}/>
</View>
)
}
}
export default Main;

ButtonComponent.js

import React from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
const styles = StyleSheet.create({
buttonContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
buttonBorder: {
borderColor: 'grey',
borderWidth: 1,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 35,
width: 70,
height: 70,
backgroundColor: 'grey'
},
})
const ButtonComponent = ({ onPress }) => (
<TouchableOpacity onPress={onPress} style={styles.buttonContainer}>
<View style={styles.buttonBorder}>
<Icon
name='plus'
size={35}
color='white'/>
</View>
</TouchableOpacity>
)
export default ButtonComponent;

PhotoComponent.js

import React, { Component } from 'react';
import { Dimensions, Image, StyleSheet, View } from 'react-native';
const width = Dimensions.get('window').width;
const largeContainerSize = width / 2;
const largeImageSize = width / 4;
const styles = StyleSheet.create({
container: {
flex: 3,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 10
},
containerSize: {
width: largeContainerSize,
height: largeContainerSize,
alignItems: 'center',
justifyContent: 'center',
tintColor: 'grey'
},
imageSize: {
width: largeImageSize,
height: largeImageSize,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
},
chosenImage: {
width: width / 1.25,
height: width / 1.25,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
}
})
class PhotoComponent extends Component {
renderDefault() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.containerSize}
source={require('../resources/background.png')}
/>
<Image
resizeMode='contain'
style={styles.imageSize}
source={require('../resources/camera.png')}
/>
</View>
)
}
renderImage() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.chosenImage}
source={this.props.uri}/>
</View>
)
}
render() {
const displayImage = this.props.uri ? this.renderImage() : this.renderDefault()
return (
<View style={styles.container}>
{displayImage}
</View>
)
}
}
export default PhotoComponent;

App.js

import React, { Component } from 'react';
import Main from './src/screens/Main'
class App extends Component {
render() {
return <Main />
}
}
export default App;

Using the Camera in Expo

Instead of using creating an Expo project and using the code we already wrote, we will start from scratch. This is because Expo has an API for picking an image or taking one with the phone that we will be using. To read more about it, here is the link, https://docs.expo.io/versions/latest/sdk/imagepicker/.

We will create a new project using Expo and take most of the code we have written. The only thing that will change is the code for selecting the image.

Begin by closing everything that relates to the RNCamera project. We then use the Terminal to create a new Expo project, called ExpoCamera, using the following command.

expo init ExpoCamera

When prompted to choose a template, pick blank template. Then enter the name of the project and use Yarn if you have it.

Once the project is created, copy over the App.js and src folder from RNCamera to ExpoCamera project. Before running, we will need to remove a few things. Here are how the files will look like in your ExpoCamera project.

App.js

import React, { Component } from 'react';
import Main from './src/screens/Main'
class App extends Component {
render() {
return <Main />
}
}
export default App;

Main.js

import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import PhotoComponent from '../components/PhotoComponent';
import ButtonComponent from '../components/ButtonComponent';
const styles = StyleSheet.create({
container: {
flex: 1
}
})
class Main extends Component {
render() {
return (
<View style={styles.container}>
<PhotoComponent />
<ButtonComponent />
</View>
)
}
}
export default Main;

PhotoComponent.js

import React, { Component } from 'react';
import { Dimensions, Image, StyleSheet, View } from 'react-native';
const width = Dimensions.get('window').width;
const largeContainerSize = width / 2;
const largeImageSize = width / 4;
const styles = StyleSheet.create({
container: {
flex: 3,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 10
},
containerSize: {
width: largeContainerSize,
height: largeContainerSize,
alignItems: 'center',
justifyContent: 'center',
tintColor: 'grey'
},
imageSize: {
width: largeImageSize,
height: largeImageSize,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
},
chosenImage: {
width: width / 1.25,
height: width / 1.25,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
}
})
class PhotoComponent extends Component {
renderDefault() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.containerSize}
source={require('../resources/background.png')}
/>
<Image
resizeMode='contain'
style={styles.imageSize}
source={require('../resources/camera.png')}
/>
</View>
)
}
renderImage() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.chosenImage}
source={this.props.uri}/>
</View>
)
}
render() {
const displayImage = this.props.uri ? this.renderImage() : this.renderDefault()
return (
<View style={styles.container}>
{displayImage}
</View>
)
}
}
export default PhotoComponent;

ButtonComponent.js

import React from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
const styles = StyleSheet.create({
buttonContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
buttonBorder: {
borderColor: 'grey',
borderWidth: 1,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 35,
width: 70,
height: 70,
backgroundColor: 'grey'
},
})
const ButtonComponent = ({ onPress }) => (
<TouchableOpacity onPress={onPress} style={styles.buttonContainer}>
<View style={styles.buttonBorder}>
<Icon
name='plus'
size={35}
color='white'/>
</View>
</TouchableOpacity>
)
export default ButtonComponent;

Most of what was removed was related to react-native-image-picker. Now with that out the way, save the files and run the app.

App looks great. Time to implement Expo’s ImagePicker API.

First thing we must do is install some Expo components. You will need to install Permissions, Constants, and ImagePicker by using the following command.

expo install expo-image-picker expo-permissions expo-constants

Then in Main.js, we will add the constructor with our state, upLoadSource. Then we will use a componentDidMount() function which will call another function called getPermissionAsync. This will be done to ask the user for their permission to gain access to the camera roll.

Then we will create a function called _pickImage, which will launch the camera roll and set upLoadSource to the source of the image we pick.

Last thing to do is to go to PhotoComponent and make a change to the Image component responsible for the photo we pick.

Main.js

import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
import * as Permissions from 'expo-permissions';
import PhotoComponent from '../components/PhotoComponent';
import ButtonComponent from '../components/ButtonComponent';
const styles = StyleSheet.create({
container: {
flex: 1
}
})
class Main extends Component {
constructor(props) {
super(props)
this.state = {
uploadSource: null
}
}
componentDidMount() {
this.getPermissionAsync();
}
getPermissionAsync = async () => {
if (Constants.platform.ios) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
}
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
});
console.log(result);
if (!result.cancelled) {
this.setState({ uploadSource: result.uri });
}
};
render() {
return (
<View style={styles.container}>
<PhotoComponent uri={this.state.uploadSource} />
<ButtonComponent onPress={this._pickImage}/>
</View>
)
}
}
export default Main;

PhotoComponent.js

renderImage() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.chosenImage}
source={{uri: this.props.uri}}/>
</View>
)
}

Now save the files and reload the app.

As you may have noticed, we can only select an image from the camera roll. This is because in _pickImage function, we are using launchImageLibraryAsync. This launches the camera roll and if we wanted to have an option to take a photo, we would need to add another permission request and another button to handle this.

Let’s create another button that will let us take a picture. In Main.js, copy ButtonComponent and paste it right below. We will be making changes to the onPress and will also pass it a prop for icon.

We got two buttons but that doesn’t look good. Wrap these buttons in a View component with flexDirection of row and paddingBottom of 40.

Main.js

render() {
return (
<View style={styles.container}>
<PhotoComponent uri={this.state.uploadSource} />
<View style={{ flexDirection: 'row', paddingBottom: 40 }}>
<ButtonComponent onPress={this._pickImage}/>
<ButtonComponent onPress={this._pickImage}/>
</View>
</View>
)
}

Much better. Time to make changes to the icons of these buttons. We will make the left button the camera button and will use a camera icon. For the right button, we will make it the gallery button and use an image icon.

Main.js

render() {
return (
<View style={styles.container}>
<PhotoComponent uri={this.state.uploadSource} />
<View style={{ flexDirection: 'row', paddingBottom: 40 }}>
<ButtonComponent onPress={this._pickImage} icon='camera'/>
<ButtonComponent onPress={this._pickImage} icon='image'/>
</View>
</View>
)
}

ButtonComponent.js

const ButtonComponent = ({ onPress, icon }) => (
<TouchableOpacity onPress={onPress} style={styles.buttonContainer}>
<View style={styles.buttonBorder}>
<Icon
name={icon}
size={35}
color='white'/>
</View>
</TouchableOpacity>
)

Great! The buttons look much better and the user can distinguish between the two. Time to work on onPress. For the second button we can leave it, but we need to create a new function for the other one. We also need to include another permission request.

Main.js

getPermissionAsync = async () => {
if (Constants.platform.ios) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL, Permissions.CAMERA);
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
}

We add the request for camera right after the request for camera roll.

We will use _pickImage as a guide to create the _takePhoto function. We will replace launchImageLibraryAsync with launchCameraAsync.

Main.js

_takePhoto = async () => {
let result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
});
console.log(result);
if (!result.cancelled) {
this.setState({ uploadSource: result.uri });
}
};

Last thing to do before running the app is to change the onPress of the first button. Then save the files and give it a try.

Perfect! It is working. We can use the left button to take photos, which can’t be done in the iOS simulator, or the right button to pick a photo from the camera roll.

Here is the code for the Expo project we just worked on.

Main.js

import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
import * as Permissions from 'expo-permissions';
import PhotoComponent from '../components/PhotoComponent';
import ButtonComponent from '../components/ButtonComponent';
const styles = StyleSheet.create({
container: {
flex: 1
}
})
class Main extends Component {
constructor(props) {
super(props)
this.state = {
uploadSource: null
}
}
componentDidMount() {
this.getPermissionAsync();
}
getPermissionAsync = async () => {
if (Constants.platform.ios) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL, Permissions.CAMERA);
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
}
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
});
console.log(result);
if (!result.cancelled) {
this.setState({ uploadSource: result.uri });
}
};
_takePhoto = async () => {
let result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
});
console.log(result);
if (!result.cancelled) {
this.setState({ uploadSource: result.uri });
}
};
render() {
return (
<View style={styles.container}>
<PhotoComponent uri={this.state.uploadSource} />
<View style={{ flexDirection: 'row', paddingBottom: 40 }}>
<ButtonComponent onPress={this._takePhoto} icon='camera'/>
<ButtonComponent onPress={this._pickImage} icon='image'/>
</View>
</View>
)
}
}
export default Main;

ButtonComponent.js

import React from 'react';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
const styles = StyleSheet.create({
buttonContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
buttonBorder: {
borderColor: 'grey',
borderWidth: 1,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 35,
width: 70,
height: 70,
backgroundColor: 'grey'
},
})
const ButtonComponent = ({ onPress, icon }) => (
<TouchableOpacity onPress={onPress} style={styles.buttonContainer}>
<View style={styles.buttonBorder}>
<Icon
name={icon}
size={35}
color='white'/>
</View>
</TouchableOpacity>
)
export default ButtonComponent;

PhotoComponent.js

import React, { Component } from 'react';
import { Dimensions, Image, StyleSheet, View } from 'react-native';
const width = Dimensions.get('window').width;
const largeContainerSize = width / 2;
const largeImageSize = width / 4;
const styles = StyleSheet.create({
container: {
flex: 3,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 10
},
containerSize: {
width: largeContainerSize,
height: largeContainerSize,
alignItems: 'center',
justifyContent: 'center',
tintColor: 'grey'
},
imageSize: {
width: largeImageSize,
height: largeImageSize,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
},
chosenImage: {
width: width / 1.25,
height: width / 1.25,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute'
}
})
class PhotoComponent extends Component {
renderDefault() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.containerSize}
source={require('../resources/background.png')}
/>
<Image
resizeMode='contain'
style={styles.imageSize}
source={require('../resources/camera.png')}
/>
</View>
)
}
renderImage() {
return (
<View style={styles.container}>
<Image
resizeMode='contain'
style={styles.chosenImage}
source={{uri: this.props.uri}}/>
</View>
)
}
render() {
const displayImage = this.props.uri ? this.renderImage() : this.renderDefault()
return (
<View style={styles.container}>
{displayImage}
</View>
)
}
}
export default PhotoComponent;

App.js

import React, { Component } from 'react';
import Main from './src/screens/Main'
class App extends Component {
render() {
return <Main />
}
}
export default App;

Awesome work! We create two projects, a RNCamera and ExpoCamera. These two projects use the phone’s camera to take pictures or the phone’s camera roll to display a photo on the screen. We learned how to get the user’s permission to gain access to the camera and camera roll, how to use icons with react-native-vector-icons, how to layer two images on top of each other, and how to display the photo we took or chose.

So where can you go from here? Play with the code. Change the size of the images. Or try using a video or taking a video. With what we have learned in this article you are on your way to creating an app with an awesome camera feature.

Exploring React Native – Part 3

Previously, in “Exploring React Native (Continued Part 2)”, we continued to work on our simple app. The code for the app was long and was all located in one file after the article, “Exploring React Native (Continued Part 1)”. Being that React Native uses native components as building blocks, we decided to break down each part of the app into custom components. There was a custom component for our images, texts, and buttons. Then we used React Native’s View component to create cards for each subject and learned the different ways to style components.

In this article, we will continue to work on our project implementing the TextInput component provided by React Native. Then we will use some JavaScript functions to convert the counter into the correct data type.

Let’s get started!

Built In Components

I will be working on a Mac using Visual Studio Code as my editor, run the app on the iOS simulator and will be working with the “FirstRNProject” project. If you are using Windows or are targeting Android, I will test the app on the Android emulator at the end of the article. This code will also work if you are using Expo and will also be tested later on.

If you are starting with a new React Native or Expo project or didn’t follow the previous article, here is the project structure:

Here is the code:

App.js

import React, { Component } from 'react';
import Main from './src/screens/Main'
class App extends Component {
render() {
return <Main />
}
}
export default App;

Main.js

import React, { Component } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import OurImage from '../components/OurImage';
import Question from '../components/Question';
import Counter from '../components/Counter';
import OurButton from '../components/OurButton';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#bff0d4',
paddingTop: 20
},
cardStyle: {
borderColor: '#535B60',
borderWidth: 2,
margin: 20,
borderRadius: 10,
},
buttonRow: {
flexDirection: 'row',
alignSelf: 'center'
}
});
class Main extends Component {
state = {
raccoons: 0,
pigeons: 0
};
//Raccoon Functions
addRaccoons = () => {
this.setState({
raccoons: this.state.raccoons + 1
})
}
removeRaccoons = () => {
if(this.state.raccoons !== 0){
this.setState({
raccoons: this.state.raccoons - 1
})
}
}
//Pigeon Functions
addPigeons = () => {
this.setState({
pigeons: this.state.pigeons + 1
})
}
removePigeons = () => {
if(this.state.pigeons !== 0){
this.setState({
pigeons: this.state.pigeons - 1
})
}
}
render() {
return (
<ScrollView style={styles.container}>
{/* Raccoon */}
<View style={styles.cardStyle}>
<OurImage imageSource={require('../img/raccoon.png')} />
<Question question='How many raccoons did you see last night?' />
<Counter count={this.state.raccoons} />
{/* Raccoon Button */}
<View style={styles.buttonRow}>
<OurButton buttonColor='#9FC4AD'
onPressed={this.addRaccoons}
text='PLUS'
/>
<OurButton buttonColor='#BAAAC4'
onPressed={this.removeRaccoons}
text='MINUS'
/>
</View>
</View>
{/* Pigeon */}
<View style={[styles.cardStyle, {marginBottom: 60}]}>
<OurImage imageSource={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />
<Question question='How many pigeons did you see today?' />
<Counter count={this.state.pigeons} />
{/* Pigeon Buttons */}
<View style={styles.buttonRow}>
<OurButton buttonColor='#9FC4AD'
onPressed={this.addPigeons}
text='PLUS'
/>
<OurButton buttonColor='#BAAAC4'
onPressed={this.removePigeons}
text='MINUS'
/>
</View>
</View>
</ScrollView>
)
}
}
export default Main;

OurImage.js

import React from 'react';
import { Image, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
image: {
height: 200,
width: 200,
alignSelf: 'center'
}
})
const OurImage = ({ imageSource }) => (
<Image style={styles.image} resizeMode='contain' source={imageSource} />
);
export default OurImage;

Question.js

import React from 'react';
import { StyleSheet, Text } from 'react-native';
const styles = StyleSheet.create({
question: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 10
},
})
const Question = ({ question }) => (
<Text style={styles.question}>{question}</Text>
);
export default Question;

Count.js

import React from 'react';
import { StyleSheet, Text } from 'react-native';
const styles = StyleSheet.create({
number: {
fontSize: 60,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 10
},
})
const Counter = ({ count }) => (
<Text style={styles.number} >{count}</Text>
);
export default Counter;

OurButton.js

import React from 'react';
import { StyleSheet, Text, TouchableOpacity } from 'react-native';
const styles = StyleSheet.create({
buttonStyling: {
width: 150,
borderRadius: 10,
margin: 5,
alignSelf: 'center'
},
buttonText: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60'
},
})
const OurButton = ({ buttonColor, onPressed, text }) => (
<TouchableOpacity onPress={onPressed} style={[styles.buttonStyling, {backgroundColor:buttonColor}]} >
<Text style={styles.buttonText}>{text}</Text>
</TouchableOpacity>
);
export default OurButton;

Here is how the app looked:

The app looks great, the code is clean and we have custom components. What we will be doing is giving the user the option to change the counter with the keyboard. This will be done with React Native’s TextInput component. According to React Native’s documentation, “A foundational component for inputting text into the app via a keyboard. Props provide configurability for several features, such as auto-correction, auto-capitalization, placeholder text, and different keyboard types, such as a numeric keypad.”

Open the “Counter.js” file and import TextInput from React Native. Then delete the Text component and replace that with the TextInput component like this:

import React from 'react';
import { StyleSheet, Text, TextInput } from 'react-native';
const styles = StyleSheet.create({
number: {
fontSize: 60,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 10
},
})
const Counter = ({ count }) => (
<TextInput />
);
export default Counter;

Save the file and reload.

Hey what happened to the zero? Well, TextInput requires a that a value prop be passed. Give the component a prop of “value” that is equal to the count.

<TextInput value={count} />

Nothing appears. If you look at the bottom of the screen, you will see that there is a warning. The warning says that the value of TextInput must be a string. In order for the TextInput component to work, we will need to use some JavaScript. The plan is to change the data from a number to a string. Then when the buttons are pressed we will convert the string to number then back to string. Hopefully this works.

Start by changing the data in state from a number to a string. Go to “Main.js” and simply put the quotes around the zero, like this:

state = {
raccoons: '0',
pigeons: '0'
};

Save and reload the file to see that the zeroes appear again.

We have lost our styling. Let’s add styling to the TextInput component in “Counter.js” by passing the style prop.

const styles = StyleSheet.create({
number: {
fontSize: 60,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 10
},
})
const Counter = ({ count }) => (
<TextInput
style={styles.number}
value={count}
/>
);

If we save the file and reload the app, the zeroes will appear with the styling we had before.

But if you try using the “PLUS” button, it will concatenate a one to the end of the text every time you press it. And if you use the “MINUS” button, the text will disappear and a warning will pop up.

For the raccoon section I used the “PLUS” button and for the pigeon section I used the “MINUS” button. These were my results:

Go to “Main.js” and we will start with the “addRaccoons” function. Create a variable named “num”, before “this.setState”. This variable will be equal to “parseInt({this.state.raccoons}) + 1”.  JavaScript comes with some built in functions, similarly to how React Native comes with built in components. We are using the function “parseInt()” to convert “{this.state.raccoons}” from a string to a number, then add one. After, we will set “num” equal to “num.toString()”. Here we are using another JavaScript function “toString()”. This function converts a number to a string. Now that “num” is a string again, we can now use “this.setState” to have “raccoons” set to “num”.

//Raccoon Functions
addRaccoons = () => {
let num = parseInt(this.state.raccoons) + 1;
num = num.toString();
this.setState({
raccoons: num
})
}

Save the file and reload the app:

Cool! The button is working and we can implement this to the “addPigeons” function, just remember to use “{this.state.pigeons}”. Now the “PLUS” buttons for the raccoon and pigeon section will work but the “MINUS” will still cause the app to give a warning.

//Pigeon Functions
addPigeons = () => {
let num = parseInt(this.state.pigeons) + 1;
num = num.toString();
this.setState({
pigeons: num
})
}

Go to “removeRaccoons” and start by creating a variable named “num”. This variable will be equal to “parseInt(this.state.raccoons)”. Then replace “{this.state.raccoons}” with “num” in the if condition. If “num” is not equal to zero, set “num” to “num – 1” and then convert it to a string. Last thing to do is set “{this.state.raccoons}” to “num”.

Here is the code:

removeRaccoons = () => {
let num = parseInt(this.state.raccoons);
if(num !== 0){
num = num - 1;
num = num.toString();
this.setState({
raccoons: num
})
}
}

The counter for the raccoon is working again. Let’s go and add this logic to the “removePigeons” function. Again, remember to use “this.state.pigeons” or the button will not work correctly.

Here are the four functions for the raccoon and pigeon buttons:

//Raccoon Functions
addRaccoons = () => {
let num = parseInt(this.state.raccoons) + 1;
num = num.toString();
this.setState({
raccoons: num
})
}
removeRaccoons = () => {
let num = parseInt(this.state.raccoons);
if(num !== 0){
num = num - 1;
num = num.toString();
this.setState({
raccoons: num
})
}
}
//Pigeon Functions
addPigeons = () => {
let num = parseInt(this.state.pigeons) + 1;
num = num.toString();
this.setState({
pigeons: num
})
}
removePigeons = () => {
let num = parseInt(this.state.pigeons);
if(num !== 0){
num = num - 1;
num = num.toString();
this.setState({
pigeons: num
})
}
}

Next what we want to do is choose the keyboard type of TextInput component. By default, the keyboard consist of the alphabet but we don’t need letters.

Go back to “Counter.js” and pass the TextInput component the following prop, “keyboard=’numeric’”.

<TextInput
style={styles.number}
value={count}
keyboardType='numeric'
/>

To test that the correct keyboard appears, save and reload the app. Then press on zero and the keyboard will appear. If the keyboard does not appear in the iOS simulator, click on “Hardware” menu and head to “Keyboard”. Then select “Toggle Software Keyboard”. Or on your computer’s keyboard, press “Command” and “K”.

It looks fine when editing the raccoon’s counter but we can’t see the text field when editing the pigeon’s counter. We need the text fields to move up when the keyboard pops up. Luckily, React Native has a component named KeyboardAvoidingView which we can use. This component, according to the React Native documentation, “is a component to solve the common problem of views that need to move out of the way of the virtual keyboard. It can automatically adjust either its position or bottom padding based on the position of the keyboard.”

First, import KeyboardAvoidingView from React Native. Then inside the render function, wrap the entire JSX code with KeyboardAvoidingView. Give this component a style prop equal to “flex: 1” and a behavior prop equal to “padding”.

import { KeyboardAvoidingView, ScrollView, StyleSheet, View } from 'react-native';
<KeyboardAvoidingView style={{ flex: 1 }} behavior="padding">
<ScrollView style={styles.container}>
{/* Raccoon */}
<View style={styles.cardStyle}>
<OurImage imageSource={require('../img/raccoon.png')} />
<Question question='How many raccoons did you see last night?' />
<Counter count={this.state.raccoons} />
{/* Raccoon Button */}
<View style={styles.buttonRow}>
<OurButton buttonColor='#9FC4AD'
onPressed={this.addRaccoons}
text='PLUS'
/>
<OurButton buttonColor='#BAAAC4'
onPressed={this.removeRaccoons}
text='MINUS'
/>
</View>
</View>
{/* Pigeon */}
<View style={[styles.cardStyle, {marginBottom: 60}]}>
<OurImage imageSource={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />
<Question question='How many pigeons did you see today?' />
<Counter count={this.state.pigeons} />
{/* Pigeon Buttons */}
<View style={styles.buttonRow}>
<OurButton buttonColor='#9FC4AD'
onPressed={this.addPigeons}
text='PLUS'
/>
<OurButton buttonColor='#BAAAC4'
onPressed={this.removePigeons}
text='MINUS'
/>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>

Save the file and reload the app. Try selecting the text input for the pigeon and notice that it moves up above the keyboard.

Much better! Now, we need to work on the handling the user input. If you are to press a number, you will see that the zero remains. TextInput has a prop called onChangeText, which we need to implement.

Go to “Counter.js” and add the prop onChangeText, we will set this equal to “handleText”. “handleText” will be a prop that is passed to “Counter.js” from “Main.js”

Counter.js

const Counter = ({ count, handleText }) => (
<TextInput
style={styles.number}
value={count}
keyboardType='numeric'
onChangeText={handleText}
/>
);

Then in “Main.js”, head to the Counter component and give it the prop “handleText”. We will have this prop equal an arrow function which takes the users input and sets the state equal to it.

Main.js

<Counter
count={this.state.raccoons}
handleText={(text) => this.setState({ raccoons: text})}
/>

Now when we use the keyboard to enter a number, the text will change.

Cool! We can change the text by pressing on the keyboard. Yes, the zero is front of the numbers doesn’t look nice but it is working. We can even use our buttons to increase or decrease the value. We won’t worry about the zero for now, instead let’s implement the “handleText” for the pigeon section.

<Counter
count={this.state.pigeons}
handleText={(text) => this.setState({ pigeons: text})}
/>

Save the file and reload to test the pigeon section.

Great! It works here too. At this point we know the app works on the iOS simulator, let’s go ahead and test it on Android first then in Expo.

Here is how it looks on Android:

Woah! That was unexpected. If we go back to the React Native document on behavior prop for KeyboardAvoidingView, it states that, “Note: Android and iOS both interact with this prop differently. Android may behave better when given no behavior prop at all, whereas iOS is the opposite.” Therefore, it is the behavior prop that is passed to KeyboardAvoidingView that is causing the spacing between the keyboard and the text input.

What we can do is check on which phone the app is running. We first import Platform and create a variable called “paddingBehavior”. This variable will check to see if the app is running on iOS and if it is then “paddingBehavior = ‘padding’”, else it is equal to ‘’. Using this variable, have “behavior={paddingBehavior}”.

import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native';
const paddingBehavior = Platform.OS === 'ios' ? 'padding' : '';
<KeyboardAvoidingView style={{ flex: 1 }} behavior={paddingBehavior}>

Save the file and reload the app.

Works much better! Time to test on Expo. After copying the code into the Expo project and running the app, here is what I got:

Nice! The app is working great in Expo as well. Here are the two files worked on throughout this article.

Main.js

1. import React, { Component } from 'react';
2. import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, View } from 'react-native';
import OurImage from '../components/OurImage';
import Question from '../components/Question';
import Counter from '../components/Counter';
import OurButton from '../components/OurButton';
const paddingBehavior = Platform.OS === 'ios' ? 'padding' : '';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#bff0d4',
paddingTop: 20
},
cardStyle: {
borderColor: '#535B60',
borderWidth: 2,
margin: 20,
borderRadius: 10,
},
buttonRow: {
flexDirection: 'row',
alignSelf: 'center'
}
});
class Main extends Component {
state = {
raccoons: '0',
pigeons: '0'
};
//Raccoon Functions
addRaccoons = () => {
let num = parseInt(this.state.raccoons) + 1;
num = num.toString();
this.setState({
raccoons: num
})
}
removeRaccoons = () => {
let num = parseInt(this.state.raccoons);
if(num !== 0){
num = num - 1;
num = num.toString();
this.setState({
raccoons: num
})
}
}
//Pigeon Functions
addPigeons = () => {
let num = parseInt(this.state.pigeons) + 1;
num = num.toString();
this.setState({
pigeons: num
})
}
removePigeons = () => {
let num = parseInt(this.state.pigeons);
if(num !== 0){
num = num - 1;
num = num.toString();
this.setState({
pigeons: num
})
}
}
render() {
return (
<KeyboardAvoidingView style={{ flex: 1 }} behavior={paddingBehavior}>
<ScrollView style={styles.container}>
{/* Raccoon */}
<View style={styles.cardStyle}>
<OurImage imageSource={require('../img/raccoon.png')} />
<Question question='How many raccoons did you see last night?' />
<Counter
count={this.state.raccoons}
handleText={(text) => this.setState({ raccoons: text})}
/>
{/* Raccoon Button */}
<View style={styles.buttonRow}>
<OurButton buttonColor='#9FC4AD'
onPressed={this.addRaccoons}
text='PLUS'
/>
<OurButton buttonColor='#BAAAC4'
onPressed={this.removeRaccoons}
text='MINUS'
/>
</View>
</View>
{/* Pigeon */}
<View style={[styles.cardStyle, {marginBottom: 60}]}>
<OurImage imageSource={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />
<Question question='How many pigeons did you see today?' />
<Counter
count={this.state.pigeons}
handleText={(text) => this.setState({ pigeons: text})}
/>
{/* Pigeon Buttons */}
<View style={styles.buttonRow}>
<OurButton buttonColor='#9FC4AD'
onPressed={this.addPigeons}
text='PLUS'
/>
<OurButton buttonColor='#BAAAC4'
onPressed={this.removePigeons}
text='MINUS'
/>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
)
}
}
export default Main;

Counter.js

import React from 'react';
import { StyleSheet, Text, TextInput } from 'react-native';
const styles = StyleSheet.create({
number: {
fontSize: 60,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 10
},
})
const Counter = ({ count, handleText }) => (
<TextInput
style={styles.number}
value={count}
keyboardType='numeric'
onChangeText={handleText}
/>
);
export default Counter;

These two files were the only files we worked on in this article, if you need the others, please check the beginning of the article.

Great job! We added the TextInput component to allow a user to use the keyboard to edit the counter data. We also used some JavaScript functions to convert the counter from a string to a number and back to a string because TextInput only worked with strings. The buttons still work and can be used to controlled the counter. We also added KeyboardAvoidingView to allow us to always see the text input field when the keyboard pops up. This caused an issue on Android because different props have different affects on specific platforms. To resolve this issue, we created a variable that checked the platform on which the app is running on.

Until next time, please try to go over the code and make changes to better understand the topics that were covered here.

Exploring React Native – Part 1.1

In the last article, titled “Exploring React Native”, we used a few components to create a simple app. The app consisted of an Image component, a couple Text components, data that changed with user interaction, and a couple of buttons created with the Button component and TouchableOpacity component.  We styled each component and at the end, had a counter app.

But there are a lot of components we did not cover and the ones we did cover, can be used in other ways. So, in this article, we will continue to use the project from the previous article to learn more about React Native’s components. The components we will be focusing on in this article are the ScrollView and View components. The ScrollView component is similar to View but allows for scrolling. The View component is one that we used in the previously but in this article, we will be using it to create sections in the app. As well, we will be passing a network image to the Image component and will learn a bit more about styling.

Let’s get started!

More Built In Components

I will be working on a Mac using Visual Studio Code as my editor, run the app on the iOS simulator and will be working with the “FirstRNProject” project. If you are using Windows or are targeting Android, I will test the app on the Android emulator at the end of the article. This code will also work if you are using Expo and will also be tested later on.

Open the App.js file and this is what we have from last time:

import React, { Component } from 'react';
import { Button, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#bff0d4',
alignItems: 'center',
},
image: {
height: 200,
width: 200,
marginTop: 100,
marginBottom: 20
},
question: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 20
},
number: {
fontSize: 60,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 20
},
plusButton: {
backgroundColor: '#9FC4AD',
width: 200,
borderRadius: 10,
margin: 10
},
minusButton: {
backgroundColor: '#BAAAC4',
width: 200,
borderRadius: 10,
margin: 10
},
buttonText: {
fontSize: 40,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60'
}
});
class App extends Component {
state = {
raccoons: 0
};
addMore = () => {
this.setState({
raccoons: this.state.raccoons + 1
})
}
removeOne = () => {
if(this.state.raccoons !== 0){
this.setState({
raccoons: this.state.raccoons - 1
})
}
}
render() {
return (
<View style={styles.container}>
<Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
<Text style={styles.question} >How many raccoons did you see last night?</Text>
<Text style={styles.number}>{this.state.raccoons}</Text>
<TouchableOpacity onPress={this.addMore} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removeOne} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>
)
}
}
export default App;

First a quick recap of the code above.

We first imported the components we were going to use from React Native. These components were the Button, Image, StyleSheet, Text, TouchableOpacity and View components. Then we created a styles variables that contained all the styling objects we used to style the components. After that, we created a state for the counter which would change with the pressing of either “PLUS” or “MINUS” buttons. Then inside the render function we had a View component that wrapped the Image, Text and TouchableOpacity components and each was styled accordingly.

Open the Terminal or Command Prompt to run the project. If you are using Visual Studio Code, there is an “integrated terminal, initially starting at the root of your workspace.” Using this terminal, you can run the React Native iOS/Android start commands, or if using Expo, the Expo start command from the editor. You can learn more about Visual Studio Code’s terminal here, https://code.visualstudio.com/docs/editor/integrated-terminal. What you will have is the following:

The app looks great but what if we wanted to create a list of animals, each with its own image, text, counter and buttons? Well, let’s copy the components between the View tags and paste them right after the “MINUS” button but before the closing View tag. Your code will look like this:

<View style={styles.container}>
{/* Raccoon One */}
<Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
<Text style={styles.question} >How many raccoons did you see last night?</Text>
<Text style={styles.number}>{this.state.raccoons}</Text>
<TouchableOpacity onPress={this.addMore} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removeOne} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
{/* Raccoon Two */}
<Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
<Text style={styles.question} >How many raccoons did you see last night?</Text>
<Text style={styles.number}>{this.state.raccoons}</Text>
<TouchableOpacity onPress={this.addMore} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removeOne} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>

When you save the file and reload the app, you will see this:

That doesn’t look right. We can barely see that there is another image of a raccoon at the bottom of the screen and we can’t scroll down to view the rest of the app. In order for the app to scroll we will use the ScrollView component. According to the React Native documentation, “The ScrollView is a generic scrolling container that can host multiple components and views.” There is another option we could use, called FlatList. The difference between the two, is that, ScrollView renders all of its children components, or the components between its tags, at once. While FlatList renders its items when they appear on the screen and removes them once off the screen. Therefore, if you have a large list, using ScrollView will slow down rendering and increase memory usage. For this app, our list will be short and ScrollView will be used but in later articles we will be using the FlatList component.

Let’s first import the ScrollView component and replace the View tags with ScrollView, save the file and reload. This is what will happen:

This error occurs because of the layout prop “alignItems: ‘center’” that is passed to the styling of the ScrollView components. To fix it remove “alignItems: ‘center’” from “container” in styles. Save the file, reload and now the app will look like this:

It’s not perfect but we can now scroll to through the app and see the second image, along with the text and buttons. To fix the styling of the images and buttons simply add “alignSelf: ‘center’” to the “image”, “plusButton” and “minusButton” styles.

image: {
height: 200,
width: 200,
marginTop: 100,
marginBottom: 20,
alignSelf: 'center'
},
plusButton: {
backgroundColor: '#9FC4AD',
width: 200,
borderRadius: 10,
margin: 10,
alignSelf: 'center'
},
minusButton: {
backgroundColor: '#BAAAC4',
width: 200,
borderRadius: 10,
margin: 10,
alignSelf: 'center'
},

Great! Everything is now centered and we can scroll.

It doesn’t make sense to keep track of the number of raccoons twice, so let’s find an image of another animal online.

If you recall, in the previous article we saved the image of the raccoon in our project under the ‘img’ folder, then passed it to the Image component. By doing so we were using a static image. The Image component can display various types of images and what I want to do now is use a network image. I went online and found an image of a pigeon and got the URL to the image.

Now if you replace the location of the second raccoon image with an URL, like this:

<Image style={styles.image} resizeMode='contain' source={require('https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png')} />

Then you will get an error like this:

That’s because, passing a network image is a little different than passing a static image. Inside of “source={}”, replace it with “{uri: ‘URL_OF_THE_IMAGE’}”. It will look like this:

<Image style={styles.image} resizeMode='contain' source={{ uri: ‘URL_OF_THE_IMAGE’ }} />

So if we replace ‘URL_OF_THE_IMAGE’ with the actual URL, we will have this:

<Image style={styles.image} resizeMode='contain' source={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />

Another key difference between static and network images is that, network images require that the dimensions of the image be specified. Our ‘image’ style object has specified width and height dimension, so our image will appear. Had those dimension not been there, the image would not be displayed.

Save the file and reload the app to get this:

Now I know not everyone likes this bird but I liked how it looked and it a bird, I would say, most people see on a regular basis. You can choose another bird or another animal entirely, it’s totally up to you. There’s just a couple more things that need changing, such as the text, adding the new counter data to the state and creating new functions for the new pigeon buttons.

Changing the text is simple, go to the Text component that corresponds to the second animal, in my case the pigeon, and change it to something like, “How many pigeons did you see today?”. Then add “pigeons: 0” to the state and replace “{this.state.raccoons}” with “{this.state.pigeons}” in the following Text component. Next we can rename the existing functions for the buttons to “addRaccoons” and “removeRaccoons” then copy and paste them right below. For the second set of functions, replace “raccoons” with “pigeons”. Remember to keep the camel case coding style and capitalize the “P” in pigeon. Also don’t forget to go to the TouchableOpacity components and rename the functions accordingly. If you are having any issues, here is the code:

class App extends Component {
state = {
raccoons: 0,
pigeons: 0
};
//Raccoon Functions
addRaccoons = () => {
this.setState({
raccoons: this.state.raccoons + 1
})
}
removeRaccoons = () => {
if(this.state.raccoons !== 0){
this.setState({
raccoons: this.state.raccoons - 1
})
}
}
//Pigeon Functions
addPigeons = () => {
this.setState({
pigeons: this.state.pigeons + 1
})
}
removePigeons = () => {
if(this.state.pigeons !== 0){
this.setState({
pigeons: this.state.pigeons - 1
})
}
}
render() {
return (
<ScrollView style={styles.container}>
{/* Raccoon */}
<Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
<Text style={styles.question} >How many raccoons did you see last night?</Text>
<Text style={styles.number}>{this.state.raccoons}</Text>
<TouchableOpacity onPress={this.addRaccoons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removeRaccoons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
{/* Pigeon */}
<Image style={styles.image} resizeMode='contain' source={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />
<Text style={styles.question} >How many pigeons did you see today?</Text>
<Text style={styles.number}>{this.state.pigeons}</Text>
<TouchableOpacity onPress={this.addPigeons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removePigeons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</ScrollView>
)
}
}

I’ve added comments, “{/* Raccoon */}” and “{/* Pigeon */}”, to help indicate that the following set of code corresponds to a particular animal. Writing comments can help you identify sections of code, especially once the code starts to get long and complicated. One thing to note is that when commenting inside JSX, where tags are used like in the above, you need to wrap the comment inside of “{/* YOUR_COMMENT */}”. Outside of JSX, you can use “// YOUR_COMMENT” for a single line comment or “/* YOUR_COMMENT */” for a multi-line comment.

Once saved and reloaded, you will be able to scroll through the app and press the buttons to increase or decrease the counters. Here is how it will look:

Great! Let’s now work on styling the app a bit more by using React Native’s View component.

Right now the app is one continuous page with images, text and buttons. To help separate each section and make the app more user friendly, we will create a border around the raccoon and pigeon set of components. It’s like creating a card for each animal and the card contains all the content for that one subject. This can simply be done by wrapping each set of components in a View and passing it a set of styles.

First, import the View component if you deleted it and create two set of opening and closing View tags. Then copy the set of raccoon components and paste them inside the first View. Repeat for the pigeon components but paste those in the second View. This is what you should have:

<ScrollView style={styles.container}>
{/* Raccoon */}
{/* First View */}
<View>
<Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
<Text style={styles.question} >How many raccoons did you see last night?</Text>
<Text style={styles.number}>{this.state.raccoons}</Text>
<TouchableOpacity onPress={this.addRaccoons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removeRaccoons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>
{/* Pigeon */}
{/* Second View */}
<View>
<Image style={styles.image} resizeMode='contain' source={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />
<Text style={styles.question} >How many pigeons did you see today?</Text>
<Text style={styles.number}>{this.state.pigeons}</Text>
<TouchableOpacity onPress={this.addPigeons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removePigeons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>
</ScrollView>

We will now create a new set of styles called “cardStyle” and pass it to both View components. To “cardStyle”, we will add a border color and width. This will create the border around each set of components.

cardStyle: {
borderColor: '#535B60',
borderWidth: 2
}

Save the file and reload the app. Wait a minute, this doesn’t look right.

I can see that there is a line separating the raccoon and pigeon cards but that’s about it. We will need to style this some more.

First add “margin: 20” to “cardStyle”, this will create space between the outside of the border and the edge of the screen. We can then go into the “image” style and remove both margins.

Looking better but I don’t like the border, it’s too boxy. This is a quick fix, add “borderRadius: 10” to “cardStyle”. Also notice that the top border of the raccoon card is being cut off by the iPhone X notch. Let’s add “paddingTop: 20” to the “container” style.

Looking awesome! Our styling is as follows:

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#bff0d4',
paddingTop: 20
},
image: {
height: 200,
width: 200,
alignSelf: 'center'
},
question: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 20
},
number: {
fontSize: 60,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60',
padding: 20
},
plusButton: {
backgroundColor: '#9FC4AD',
width: 200,
borderRadius: 10,
margin: 10,
alignSelf: 'center'
},
minusButton: {
backgroundColor: '#BAAAC4',
width: 200,
borderRadius: 10,
margin: 10,
alignSelf: 'center'
},
buttonText: {
fontSize: 40,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60'
},
cardStyle: {
borderColor: '#535B60',
borderWidth: 2,
margin: 20,
borderRadius: 10,
}
});

Before continuing with styling, I would like to over the different ways you can style a component.

Although not mentioned before, you can simply pass styles to a component using inline styling. Here is an example:

<View style={{ flex: 1, borderColor: ‘red’, borderWidth: 2 }} >

Or, as we have been doing, we can put all the styling in one location and reference it when needed like this:

const styles = StyleSheet.create({
container: {
flex: 1,
borderColor: ‘red’,
borderWidth: 2
}
});
<View style={styles.cardStyle}>

Both styling methods will style the component the same, but by having the styling outside of the render function, we are making the code cleaner and easier to read.

We can also mix and use both by passing style an array, which can help has pass specific styling to only one particular component, while also passing a set of styles other components use. Here is an example:

<View style={[ styles.container, { margin: 20 } ]} >

The styling is almost perfect but I want to add a bottom margin to the pigeon’s View component because I want some spacing between the bottom of the screen and the bottom of the pigeon card. We will pass “cardStyle” and “marginBottom: 60” to only the second View component. Here is how it is done:

{/* Pigeon */}
{/* Second View */}
<View style={[styles.cardStyle, {marginBottom: 60}]}>
<Image style={styles.image} resizeMode='contain' source={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />
<Text style={styles.question} >How many pigeons did you see today?</Text>
<Text style={styles.number}>{this.state.pigeons}</Text>
<TouchableOpacity onPress={this.addPigeons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removePigeons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>

The bottom of the pigeon card is visible. This is looking great!

Continuing to focus on styling, let’s have the two buttons placed next to each other. Here we will be using the View component again. First inside the raccoon’s View component create a View right after the counter text. Then copy and paste both TouchableOpacity components inside of the View tags. Then inside the pigeon’s View component, do the same for those buttons.

<ScrollView style={styles.container}>
{/* Raccoon */}
<View style={styles.cardStyle}>
<Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
<Text style={styles.question} >How many raccoons did you see last night?</Text>
<Text style={styles.number}>{this.state.raccoons}</Text>
{/* Raccoon Buttons */}
<View>
<TouchableOpacity onPress={this.addRaccoons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removeRaccoons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>
</View>
{/* Pigeon */}
<View style={[styles.cardStyle, {marginBottom: 60}]}>
<Image style={styles.image} resizeMode='contain' source={{ uri: 'https://cdn.pixabay.com/photo/2012/04/02/12/43/pigeon-24391_1280.png' }} />
<Text style={styles.question} >How many pigeons did you see today?</Text>
<Text style={styles.number}>{this.state.pigeons}</Text>
{/* Pigeon Buttons */}
<View>
<TouchableOpacity onPress={this.addPigeons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removePigeons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>

If you save the file and reload the app, you will notice that nothing happens. We will need to create a new style called “buttonRow”, pass it “flexDirection: ‘row’”, then pass this style to the buttons’ View components. By default “flexDirection” is set to column, because of this, components are stacked on top of each other. But by setting “flexDirection” to row, the components in that View will be stacked side by side.

buttonRow: {
flexDirection: 'row'
}
{/* Raccoon Button */}
<View style={styles.buttonRow}>
<TouchableOpacity onPress={this.addRaccoons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removeRaccoons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>
{/* Pigeon Buttons */}
<View style={styles.buttonRow}>
<TouchableOpacity onPress={this.addPigeons} style={styles.plusButton} >
<Text style={styles.buttonText}>PLUS</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.removePigeons} style={styles.minusButton}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>
</View>

Now the app looks like this:

Oh no! The buttons don’t fit properly. Let’s make the buttons smaller by changing the text’s size and decreasing its width.

plusButton: {
backgroundColor: '#9FC4AD',
width: 150,
borderRadius: 10,
margin: 10,
alignSelf: 'center'
},
minusButton: {
backgroundColor: '#BAAAC4',
width: 150,
borderRadius: 10,
margin: 10,
alignSelf: 'center'
},
buttonText: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60'
},

Better but still needs work. We can decrease the button’s margins and center the button’s View component.

plusButton: {
backgroundColor: '#9FC4AD',
width: 150,
borderRadius: 10,
margin: 5,
alignSelf: 'center'
},
minusButton: {
backgroundColor: '#BAAAC4',
width: 150,
borderRadius: 10,
margin: 5,
alignSelf: 'center'
},
buttonText: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
color: '#535B60'
},
buttonRow: {
flexDirection: 'row',
alignSelf: 'center'
}

At this point, the app looks wonderful but I have tested our code only on the iOS simulator. I want to make sure for those using Windows or targeting Android, that we have the same results. After opening the Android emulator and running the project, I have this:

The app works! I knew it would but there are times that certain components appear differently on iOS than they do on Android. We saw these differences when working with React Native’s Button component. But in this case, the app looks and works the same on both Android and iOS.

Now for those using Expo, I mentioned at the beginning that the code used in this project would also work for you guys. To ensure this is true, I am going to copy the code and paste it in an Expo project we created a while back called, “FirstExpoProject”. Here is the app on a real iPhone X Max:

Yes, app works and looks great! It could probably use more padding at the top and bottom of the screen. Unfortunately, I do not own an Android device but since the app worked on the Android emulator, I am certain it will work on an Android device.

This is where we will leave off for this article. We added ScrollView to our app, allowing us to the ability to scroll and add more content. Then we added a new animal and passed a network image to the Image component. Lastly, using View and some new styling skills we created cards to contain each subject. I suggest you play around with the code because following steps is one thing but when you try it on your own, that’s when you really learn.

In the next article, we will continue to expand our React Native skills using this project. The code in this project was getting long and could be cleaned up. For one, we will learn about creating components and a few more things. See you in the next article.

Exploring React Native

In the previous article, titled “Getting Started with React Native”, we briefly went over what React Native is. We went through the installation process for the Expo CLI and the React Native CLI. The last thing we did was create an Expo and React Native project and made changes to the code, then run the projects.

But at the end of the article you may have wondered, “Now what?”. In this article I will show you some of the built in components that come with React Native. We will use these components to create a fairly simply one screen app that will keep track of a number that can be increased and decreased by a button. The app will teach you how to add an image, text, buttons, keep track of data and style all these components.

Let’s begin!

Built In Components

Begin by opening your project in the code editor of your choice, remember I use Visual Studio Code. If you recall, in the last article there were two ways to create a project. We created an Expo project, called “FirstExpoProject,” and a React Native project, called “FirstRNProject”. I will be using the “FirstRNProject” project but the code in this article will work for both.

In the Explorer, select App.js, open it and delete all the code inside of the file. Throughout this article I will be showing you the code I write, some times it will only be a snippet of the file and I will use “…” to indicate there is more code above and/or below that snippet. At the end of the article I will post the entire code for the App.js file.

The first two line of code will be the following:

import React, { Component } from ‘react’;
import { StyleSheet, Text, View } from ‘react-native’;

The second line is important to us because this is where we will be importing all of the built in components. The StyleSheet allows us to create a specific set of styles for our components, Text component allows us to create text and the View component is one of the fundamental components for the user interface. To learn more about this components and the others provided by React Native check out this link, https://facebook.github.io/react-native/docs/components-and-apis.

Next, create a variable named styles. This is where we will create a StyleSheet and style our components.

const styles = StyleSheet.create({

)};

Time to create the class and export it. Here is how the code will look:

class App extends Component {

}
export default App;

Inside the class, we need to add the render function and return our View component. Save the file.

class App extends Component {
   render() {
     return (
     <View />
     )
   }
}

At this point we can run the app. As I mentioned before I am using the project we created with the React Native CLI, therefore I will open a Terminal on my Mac and head to the directory where the project is located and enter the following command:

react-native run-ios

If you are using Windows or simply want to test on Android, type the following into the Terminal or Command Prompt:

react-native run-android

If you decided to work with the Expo project, type the following into the Terminal or Command Prompt and use one of the methods from the previous article to run the app on your phone or simulator/emulator:

expo start

Again, I am using a Mac and for the time being will be using the iOS simulator. Later on I will use the Android emulator to point out some styling differences.

What you will see when the app is up and running is a white, blank screen. Here is the app running on the iOS simulator:

A blank, white screen looks dull, let’s change that by adding some color to the background:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#bff0d4'
  }
});

class App extends Component {
   render() {
     return (
     <View style={styles.container} />
   )
   }
}

First we add a style prop to the View component. In styles we create a style “container” and gave it a flex of 1 and a background color. “flex: 1”, simply tells the View component to take up all the space available. To learn more about flex, go here https://facebook.github.io/react-native/docs/height-and-width. The color give to the “backgroundColor” can be written in a few ways for React Native, here is a link to learn more, https://facebook.github.io/react-native/docs/colors. If you’ve ever done web development and used CSS, the way to style these components will look familiar. Save the file and if you don’t see the change, reload the simulator/emulator. You can reload by pressing the “Command” and “R” keys for the iOS simulator. For Android emulator, tap “R” twice.

Ok now that we have some color, why not add an image to our app. First thing you need is an image. I found one online of a raccoon, download it and put in a new folder I created inside the project called “img”. But you can also use different type of images, such as using an URL for an image found online. Some of types of images need to have a specific height and width passed to it. Learn more here, https://facebook.github.io/react-native/docs/image. Let’s import the Image component.

import { Image, StyleSheet, Text, View } from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#bff0d4',
    alignItems: 'center',
  }
});

class App extends Component {
  render() {
    return (
    <View style={styles.container}>
    <Image resizeMode='center' source={require('./img/raccoon.png')} />
    </View>
    )
  }
}

Inside the “container” style, I added “alignItems: ‘center’” which centers all the components inside of View horizontally. Next, inside the View component I added the Image component and gave the source the location to the image of the raccoon I am using. I also added “resizeMode=’center’” because the image was large and not displaying properly, this will center the image in the view. Here is what I end up with:

You see that the image is still too large, so we are going to change that.

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#bff0d4',
    alignItems: 'center',
  },
  image: {
    height: 200,
    width: 200
  }
});

class App extends Component {
  render() {
    return (
      <View style={styles.container}>
      <Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
      </View>
    )
  }
}

Create a new style named “image” and give it a height and width. Pass the style to the Image component and change the “resizeMode” to “contain”. This will ensure the image is resized properly to fit the height and width given.

You will notice the image is too close to the top of the screen and a piece of the ears are cut off. To fix this, add “marginTop” and give it a value of 100. You can play around with the number to get the desired spacing between the top of the screen and the image.

image: {
  height: 200,
  width: 200,
  marginTop: 100
}

Here is what we have:

Inside the View component and right below the Image component, lets add a Text component with the following text, “How many raccoons did you see last night?”.

return (
  <View style={styles.container}>
  <Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
  <Text>How many raccoons did you see last night?</Text>
  </View>
)

The text will appear right below the image but it needs some styling. Create a new style named “question” and give it a font size of 20, make it bold, center and change the color. Don’t forget to pass the prop in the Text component.

<Text style={styles.question}>How many raccoons did you see last night?</Text>
question: {
   fontSize: 20,
   fontWeight: 'bold',
   textAlign: 'center',
   color: '#535B60'
}

Looking much better. Next add a new Text component underneath the question which will track the number of raccoons you see. The styling will be similar to that of the question but the font size will be much bigger.

<Text style={styles.number} >0</Text>
number: {
   fontSize: 60,
   fontWeight: 'bold',
   textAlign: 'center',
   color: '#535B60'
}

Here is what we now have:

When we see a raccoon, we will need to be able to press a button which will increase the number or decrease the number. React Native comes with a built in component called Button and we will use it to create a button for increasing the value. Later on, we will use another component that allows for more customization.

import { Button, Image, StyleSheet, Text, View } from 'react-native';
<View style={styles.container}>
  <Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
  <Text style={styles.question} >How many raccoons did you see last night?</Text>
  <Text style={styles.number}>0</Text>
  <Button title='PLUS' color='#535B60' />
</View>

Let’s add the Button component right after the Text component display the number. We pass it a title, “PLUS”, and a color. At the moment our button does not do anything. One thing about the Button component is that they look different on iOS than they do on Android. I am going to run the Android emulator and show you the difference.

Here is the button on Android:

And here is the button on iOS:

Notice how on Android the button has “PLUS” in a white font and is surrounded by the color we provided. But on iOS only the text “PLUS” appears and it is in the color we provided. This is something you will notice when working with React Native and you will need to style your components accordingly.

Before telling the button what to do every time it is pressed, lets create a state that will store our number data. Currently, the number of raccoons seen is set to zero but we want it to change with the pressing of the button.

Create a state with “raccoons” set to zero inside of the class App and before the render function. Then use the data to replace the hard coded zero in the Text component.

class App extends Component {
  state = {
    raccoons: 0
  };

render() {
  return (
    <View style={styles.container}>
    <Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
    <Text style={styles.question} >How many raccoons did you see last night?</Text>
    <Text style={styles.number}>{this.state.raccoons}</Text>

Notice how I replaced the “0” in the second Text component with “{this.state.raccoons}”, this calls “raccoons” in the state. Change the “0” in the state to another number, save, and see that it changes.

We’re now ready to make our button do something. Create a function called “addMore” and set “raccoons” to increase by 1. Then in the Button component, add the prop “onPress” and pass it the “addMore” function.

addMore = () => {
  this.setState({
    raccoons: this.state.raccoons + 1
  })
}
<Button onPress={this.addMore} title='PLUS' color='#535B60' />

Save the file and see that when you press the button, the counter will increase by one. Here is how it will look:

We can increase the counter but what if we made a mistake and want to decrease the counter? Well, we can create another button that will do just that. This time we will use TouchableOpacity component to create a custom button. Begin by importing TouchableOpacity and adding the component right after the Button component we have in the class. We will wrap the TouchableOpacity around a Text component.

import { Button, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
<Button onPress={this.addMore} title='PLUS' color='#535B60' />

<TouchableOpacity>
<Text>MINUS</Text>
</TouchableOpacity>

Save the file and see that the button needs some styling. We will add styling to the text to change the font size but also to the TouchableOpacity component to make it stand out.

button: {
  backgroundColor: '#BAAAC4',
  width: 200,
  borderRadius: 10
},
buttonText: {
  fontSize: 40,
  fontWeight: 'bold',
  textAlign: 'center',
  color: '#535B60'
}
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>

We will give the TouchableOpacity component a background color, width of 200 and border radius of 10 to round the corners. To the Text, same as before, font size of 40, bold, align it to the center and give a color. Here is how it will look:

The “Minus” button currently does nothing. We will create a function and pass it to the TouchableOpacity component, just like we did to the Button component.

removeOne = () => {
  this.setState({
    raccoons: this.state.raccoons - 1
  })
}
<TouchableOpacity onPress={this.removeOne} style={styles.button}>
<Text style={styles.buttonText}>MINUS</Text>
</TouchableOpacity>

Test the app! You can add to the counter and remove from it as well. The “MINUS” button is working but it should stop when it reaches zero, we can’t see a negative number of raccoons. This can be easily fixed by adding an if statement to check the current value of “raccoons”.

removeOne = () => {
  if(this.state.raccoons !== 0){
    this.setState({
      raccoons: this.state.raccoons - 1
    })
  }
}

Before calling this app complete, let’s make the “PLUS” button look like the “MINUS” button and do some other styling changes.

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

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#bff0d4',
    alignItems: 'center',
  },
  image: {
    height: 200,
    width: 200,
    marginTop: 100,
    marginBottom: 20
  },
  question: {
    fontSize: 30,
    fontWeight: 'bold',
    textAlign: 'center',
    color: '#535B60',
    padding: 20
  },
  number: {
    fontSize: 60,
    fontWeight: 'bold',
    textAlign: 'center',
    color: '#535B60',
    padding: 20
  },
  plusButton: {
    backgroundColor: '#9FC4AD',
    width: 200,
    borderRadius: 10,
    margin: 10
  },
  minusButton: {
    backgroundColor: '#BAAAC4',
    width: 200,
    borderRadius: 10,
    margin: 10
  },
  buttonText: {
    fontSize: 40,
    fontWeight: 'bold',
    textAlign: 'center',
    color: '#535B60'
  }
});

class App extends Component {
  state = {
    raccoons: 0
  };

  addMore = () => {
    this.setState({
      raccoons: this.state.raccoons + 1
    })
  }
  removeOne = () => {
    if(this.state.raccoons !== 0){
      this.setState({
        raccoons: this.state.raccoons - 1
      })
    }
}

  render() {
    return (
      <View style={styles.container}>
      <Image style={styles.image} resizeMode='contain' source={require('./img/raccoon.png')} />
      <Text style={styles.question} >How many raccoons did you see last night?</Text>

      <Text style={styles.number}>{this.state.raccoons}</Text>

      <TouchableOpacity onPress={this.addMore} style={styles.plusButton} >
      <Text style={styles.buttonText}>PLUS</Text>
      </TouchableOpacity>

      <TouchableOpacity onPress={this.removeOne} style={styles.minusButton}>
      <Text style={styles.buttonText}>MINUS</Text>
      </TouchableOpacity>
      </View>
    )
  }
}

export default App;

First, replace the Button component with a TouchableOpacity component wrapped around a Text component. Pass the “buttonText” style to the Text and the “addMore” function to the TouchableOpacity. Next I wanted to differentiate the two buttons, so I created another style named “plusButton” and renamed “button” to “minusButton”, then passed them accordingly. I then changed the font size of the question and added padding and margin to some components to separate them. Here is what I was left with:

I’m not much of a designer but I am happy with the way the project came out but if there’s something you don’t like, play around with the styling. Adjust the font sizes, font color, background colors or spacing between components. There’s a lot you can do to make your app unique. You can change the image of the raccoon to something else, maybe a bird, or change the color scheme. There are a lot of options online to find royalty free images and color schemes.

In the next article, we will continue to work with this project. We will use more built in components and style them. For now, make sure you understand what was done in this article and if there’s something that doesn’t make sense visit the React Native documentation, https://facebook.github.io/react-native/.

Getting Started With React Native

React Native

In this article you will learn how to set up your computer to begin developing apps using React Native.

Before we begin, what is React Native? As stated on the official React Native website, https://facebook.github.io/react-native/, “React Native lets you build mobile apps using only JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components.” The site goes on to say, “With React Native, you don’t build a ‘mobile web app’, an ‘HTML5 app’, or a ‘hybrid app’. You build a real mobile app that’s indistinguishable from an app built using Objective-C or Java. React Native uses the same fundamental UI building blocks as regular iOS and Android apps. You just put those building blocks together using JavaScript and React.”

You can learn more by reading the documentation at https://facebook.github.io/react-native/.

One important thing to keep in mind, is that, although React Native allows you to develop iOS and Android apps, you will need to have a Mac computer to build an iOS application.

Setting Up Your Computer

Depending on your experience, skills and what you plan on doing, you may either want to develop with Expo or with the React Native CLI. What’s the difference?

Expo is the easiest and quickest way to get started and build simple apps. There is no need to use Xcode or Android studio, simply use the Expo CLI to develop your app and run it on your phone using the Expo client app.

React Native CLI, on the other hand, allows more flexibility and control. With it you can do things, such as, using third party packages that require you to run the command “react-native link”. It also allows you to go in and make changes to native iOS and Android code. But the installation process will vary depending on the operating system your computer is running. Window users be aware that you will not be able to build iOS projects because iOS development requires a Mac. On the other hand, Mac using will be able to develop for both iOS and Android.

I will be walking you though, both the Expo and React Native CLI installation. Let’s begin with Expo. If you prefer Reactive Native CLI, please skip ahead to the Reactive Native CLI installation section.

Expo Installation

To use Expo, you need to install Node. The fastest and easiest way to install Node is through the website. Head to https://nodejs.org and choose a version. LTS is more stable, more likely to work, and the recommended option. Current is the latest version of Node but may not work with React Native. Choose one, download it and follow the instructions to install. Node will also install npm. Using the Node website will work for both Mac and Window users.

Once installed you can check the version of Node by opening the Terminal or Command Prompt and type the following and then press the “Enter” key:

node -v

Node will also install npm, to check the version type:

npm -v

If you are unsure how to open Terminal or Command Prompt, here is how to do so:

To open the Terminal on a Mac, open the Applications folder, then the Utilities folder and click on Terminal. Or press “Command” and ‘space” keys to open Spotlight and search for “Terminal”, which will be under “Application” section.

To open the Command Prompt on Windows, search for cmd in the Start menu. Or press the “Windows” and “R” key to open the Run window and search for “cmd”.

Version used in this article:

Node version = v10.15.3 and npm version = 6.9.0

Next install the Expo CLI command line utility. Do so by typing the following into your Terminal or Command Prompt:

npm install -g expo-cli

The next step is not mentioned in the React Native documentation, but the Expo documentation states that you will need Git on your computer to create projects. The link to Expo’s installation documentation is located here, https://docs.expo.io/versions/latest/introduction/installation/.

 

You can download Git here, https://git-scm.com/downloads. Download the correct version for your computer.

Start the Git setup. The Mac and Windows installation process differ. The Windows setup offers much more options than the Mac setup. I am not a Windows user, so I left all the settings as default and installed. You may need to restart your computer afterwards.

 

(Git installation on Windows)

The final step is to install the Expo client app on your phone.

Download the Expo client app for iOS from the App Store, https://itunes.apple.com/app/apple-store/id982107779. Or search for “Expo Client” in the App Store.

Download the Expo client app for Android from the Google Play Store, https://play.google.com/store/apps/details?id=host.exp.exponent&hl=en_US. Or search for “Expo Client” in the Google Play Store.

Having an Expo account will open up a few more options, such as publishing projects to your Expo portfolio. You can create an Expo account through the app or through their website, https://expo.io/. This step is optional.

React Native CLI Installation

React Native CLI has a series of steps that will vary for Mac and Window users. I will start by guiding you through the steps for those using Mac, then for those using Windows.

Mac Installation

Start by installing Homebrew, head to https://brew.sh, copy the script and paste it into the Terminal.

Once installed, run the following command in the Terminal:

brew install node
brew install watchman

Installing Node will also install npm.

Next step is to install the React Native CLI by typing:

npm install -g react-native-cli

iOS Set Up

Let’s first start by downloading Xcode, which is necessary for iOS builds.

You will find Xcode at https://itunes.apple.com/us/app/xcode/id497799835?mt=12, click “View in Mac app store” to download and install. Or open the Mac App Store and search for “Xcode”.

Next, install Xcode Command Line Tools. Open Xcode and choose “Preferences…” from  the Xcode menu on the upper left corner of the screen. The General window will open, go to Locations and toward the bottom, install the Command Line Tools by selecting the latest version from the dropdown.

At this point you are ready to create your first React Native project but if you want to develop for Android too, please follow the following steps.

Android Set up

Let’s focus on downloading what’s needed for Android builds.

First thing to do is install the Java SE Development Kit (JDK). You can find it at http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html.

Next, install Android Studio, you can find it at https://developer.android.com/studio/index.html.

Once installed open Android Studio, the Android Studio Setup Wizard will open. Click “Next” and when asked to choose between “Standard” and “Custom” installation, pick “Custom”.

Click “Next” and choose the theme you would like, then click “Next”. At this point it will ask you to check the components you would like to download. According to the React Native documentation, check Android SDK, Android SDK Platform, Performance (Intel HAXM) and Android Virtual Device.

 

(I had Android Studios already install and that is why I got a warning at the bottom.)

Click “Next” and leave the Emulator settings on the recommended setting and click “Next”, then “Finish”.

There are a couple more steps left.

Open Android Studio and in the lower right side of the window, click Android Studio click on “Configure” and “SDK Manager”.

Select the “SDK Platforms” tab, make sure you have “Show Package Details” checked on the bottom right side of the window. Select Android 9.0 (Pie) and make sure Android SDK Platform 28, Intel x86 Atom_64 System Image and Google APIs Intel x86 Atom System Image are checked.

Then click on the “SDK Tools” tab, making sure “Show Package Details” is checked on the lower right side of the window. Look under “Android SDK Build-Tools 29-rc2”, make sure 28.0.3 is selected.

Now click “Apply”, accept the user agreements and install.

Last step is to add a few lines to your “.bash_profile”. Open up Terminal and type:

nano .bash_profile

Then add the following lines:

export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools

 

To save the changes simply press “Control” and “X”. You will be asked to save changes, hit “Y” and then “Enter” to exit.

You’re all set for iOS and Android development on your Mac.

Windows Installation

Begin by installing Chocolatey, which can be found at  https://chocolatey.org/install/. Copy the command under “Install with cmd.exe” and paste it into the Command Prompt.

 

 

To open the Command Prompt, search for “cmd” in your Start menu, right click on it and select “Run as administrator”. A pop up will open asking for permission, select “Yes”. Copy the command provided into the Command Prompt and install.

Ensure that it is installed by typing:

choco

This will return the version and a command for the help menu.

Then type:

choco install -y nodejs.install python2 jdk8

After that, it is time to install the React Native CLI by typing the following into the Command Prompt:

npm install -g react-native-cli

I noticed that when I tried to do this, I got an error saying “npm” was not a recognized command. Restart your computer, open cmd as an administrator and try again.

Next, install Android Studio. You can find it at https://developer.android.com/studio/index.html. Open Android Studio, the Android Studio Setup Wizard will open. Click “Next” and when asked to choose between “Standard” and “Custom” installation, pick “Custom”.

(This window, and a few of the following, are screenshots taken on a Mac.)

Click “Next” and choose the theme you would like, then click “Next”. At this point it will ask you to check the components you would like to download. According to the React Native documentation, check Android SDK, Android SDK Platform, Performance (Intel HAXM) and Android Virtual Device. Click “Next” and leave the Emulator settings on the recommended setting and click “Next”, then “Finish”.

(Ignore the warning. This image is a screenshot from my Mac and I had previously installed Android Studios.)

Open Android Studio and in the lower right side of the window, click on “Configure” and “SDK Manager”.

Select the “SDK Platforms” tab, make sure you have “Show Package Details” checked on the bottom right side of the window. Select Android 9.0 (Pie) and make sure Android SDK Platform 28, Intel x86 Atom_64 System Image and Google APIs Intel x86 Atom System Image are checked.

Then click on the “SDK Tools” tab, making sure “Show Package Details” is checked on the lower right side of the window. Look under “Android SDK Build-Tools 29-rc2”, make sure 28.0.3 is selected.

 

Next search for “Environment variables” in the Start menu, and select “Edit the system environment variables”. When the window opens, click on the “Advanced” tab and towards the bottom select the “Environment Variables”. Under “User variables for YOUR_USERNAME”, click “New” and add the following:

Variable name = “ANDROID_HOME”
Variable value = “c:\Users\YOUR_USERNAME\AppData\Local\Android\Sdk”

You can find the location of the SDK by looking in the “SDK Manager” settings of Android Studio.

 

Next select “Path” in the “User variables for YOUR_USERNAME” and select “Edit”. A new window will open, click “New” and add the following:

c:\Users\YOUR_USERNAME\AppData\Local\Android\Sdk\platform-tools

Close the “Edit environment variable”, “Environment Variables” and “System Properties” windows by clicking “Ok”.

You are all set! Let’s begin by creating an Expo project, if you decide to use only React Native, please skip ahead.

Creating Expo Project

From here on out I will be working on a Mac, the steps will be the same on Windows but I will not be using the Command Prompt. I will be using commands, such as “clear”, that will work in the Terminal but may not work in the Command Prompt.

Open up a new Terminal and head to the directory you wish to save the project in.

I like to save my projects onto my Desktop, so I will type:

cd Desktop

Once in the directory of your choice, type:

expo init FirstProject

“expo init” initializes a directory called “FirstExpoProject”. If you prefer to name the project something else, replace “FirstExpoProject” with the name of your choice.

There will be an option given to “Choose a template”, select “Blank” and press “Enter”.

Next, you are prompted to enter “The name of your app visible on your home screen”. I will type “FirstExpoProject” and press “Enter”.

Congrats! You created your first React Native app. To enter the folder type:

cd PROJECT_NAME

And the follow to start it:

expo start

Or

npm start

A new tab will open in your web browser, called the Expo Dev Tool. Depending on your computer this may take some time.

You will have a few options to view the app.

One way to view the app is to use the QR code. In the Terminal and Expo Dev Tool you will see a QR code. You can scan the code with the Expo app on Android or the iPhone’s camera.

Make sure your computer and phone are on the same network when the “Connection” in Expo Dev Tools is set to “LAN”. If you select “Tunnel”, then both devices can be on different networks.

Another way to view the app is to type “e” in the Terminal, allowing you to send an email or text message with a link to the app. You will have the same option in the browser.

And the final option I will discuss, is to open the app in the Android emulator or iOS simulator, which requires you to first install Android Studio and Xcode. If you want to do this, please look at the section titled “React Native CLI”.

Once you have it running it will look like this:

It’s now time to see what we created! You will need a text editor for this part and my personal preference is Visual Studio Code, which can be downloaded here https://code.visualstudio.com/. There are other options, such as Atom and Sublime, for example. Since I will be using Visual Studio Code, some of the following steps may differ if you are using a different editor.

Open Visual Studio Code, and open the “FirstExpoTutorial” project. On the left side of the editor you will see the “Explorer” section. If you do not see it, click on “View” at the top of the screen and then on “Explorer”.

In the “Explorer”, open up App.js file, this is where we will modify the code.

 

The first line of code imports the default export React from the module react.

The second line is importing components from React Native. React Native has a bunch of built in components and apis which you can see here, https://facebook.github.io/react-native/docs/components-and-apis.

Next comes the class App, which has the function render(), that returns JSX code, similar to HTML, and is exported.

The JSX consist of a View, a container that supports layout, and Text, a React component for displaying text. Replace the text between the <Text></Text> tags, with:

<Text>Hello World! Welcome to my new expo app!</Text>

Save the file and the app will reload with the new text.

The last block of code you will notice is the constant named styles. This is the styling that is passed to the View component and is similar to CSS styling.

Creating React Native Project

The steps to create a project using the React Native CLI are similar to Expo. Use the command “cd” to move directories. I will use the following command to move to the Desktop directory, but you can choose any directory:

cd Desktop

Once you are in the directory you wish to create the project in, type :

react-native init FirstRNProject

“react-native init” initializes a directory called “FirstRNProject”. You can choose to name your project something else.

During the initialization it may suggest that you use Yarn to speed it up. This is optional but if you want Yarn you can download it at https://yarnpkg.com/en/docs/install#mac-stable. There is a Homebrew option I will use on Mac, type the following in the terminal:

brew install yarn

In Windows either use the Installer or Chocolatey. To use Chocolatey, type the following into the Command Prompt as an administrator:

choco install yarn

Congrats! You created your first React Native app. Now it’s time to run the app. After creating the project, you may have noticed that there are instructions on how to run it. First thing to do is to enter the project folder by typing the following into the Terminal:

cd FirstRNProject

If you are on a Mac, you can run you iOS project two ways. One is to type the following into the Terminal:

react-native run-ios

This command will start building your app and will open it on the iOS simulator. Second way to run the project is to open the “YOUR_PROJECT_NAME.xcodeproj” file inside the “ios” folder of the React Native project. Xcode will open up, select the simulator and click on the run button at the top left side of the window.

This is how the app will look on the simulator:

If you are using Windows or Mac and want to run the app on Android, start by opening up Android Studios to start up the emulator before running the command in the Terminal.

Select “Open an existing Android Studio project” from the “Welcome to Android Studio” window.

Navigate to the directory where the project is located and the select the “android” folder inside and click “Open”. There may be a popup asking you to update the Android Gradle plugin. Don’t upgrade because it may cause the project not to work, I clicked “Don’t remind me again for this project”.

Before running the command in the Terminal, there is one last thing to do and that is to check the AVD Manager. Go to Tools in the top of the screen and click on “AVD Manager”. The Android Virtual Device Manager window will open. I have a device already set up using Android 9.0.

If you don’t see a device or want to create a new one, click on “Create Virtual Device”. Select the device you want and click “Next”. On the following screen select “Pie”, download if needed” and click “Next”.

Give the device a custom name or leave it as default and click “Finish”. Choose a device to run and click on the green play button under “Actions”.

Once the emulator is running, go back to the Terminal and inside the project you created run the following command:

react-native run-android

Here is how the project will look in the emulator:

It’s now time to see what we created! You will need a text editor for this part and my personal preference is Visual Studio Code, which can be downloaded here https://code.visualstudio.com/. There are other options, such as Atom and Sublime, for example. Since I will be using Visual Studio Code, some of the following steps may differ if you are using a different editor.

Open Visual Studio Code, and open the FirstTutorial project. On the left side of the editor you will see the Explorer section. If you do not see it, click on “View” at the top of the screen and then on “Explorer”.

Since the project was created with the React Native CLI, you will notice that this project has more folders than an Expo project. Two folders that stand out are the Android and iOS folders, which will give you access to native code.

In the Explorer, open up App.js file, this is where we will modify the code.

The first line of code, “import React from ‘react’”, imports the default export React from the module react.

The second line is importing components from React Native. React Native has a bunch of built in components and apis which you can see here, https://facebook.github.io/react-native/docs/components-and-apis.

The React Native project will then have a constant named “instructions”. What it does is determine which kind of phone that app is running on and will display text accordingly.

Next comes the class App, which has the function render(), that returns JSX code, similar to HTML, and is exported.

The JSX consist of a View, a container that supports layout, and a few Text, a React component for displaying text. Delete the bottom two, then replace the text between the <Text></Text> tags, with:

<Text>Hello World! Welcome to my new React Native app!</Text>

Save the file and if the text on the simulator/emulator does not change, simply reload the app. To do so on the iOS simulator press “Command” and “D” and click reload. In the Android emulator press “Command” and “M” if running it on Mac or “Control” and “M” on Windows.

The last block of code you will notice is the constant named styles. This is the styling that is passed to the View component and is similar to CSS styling.

Now What?

Congratulations! You have installed everything necessary to create a React Native project, started the project and modified the code. Take a look at the React Native documentation or Expo documentation and play around with the project. Add more text, change the background color, add an image or a button. This is just the beginning, in no time you will be creating amazing apps and publishing them for the world to see.