Building a React Native app w/ expo
Avery Berkowitz
Posted on November 25, 2019
While the design principles between building mobile and web applications share many similarities, it was been difficult for many web developers to quickly make the switch. Building mobile (native) apps has long required learning a new language like Swift, Objective-C, or Java. The Expo React Native platform aims to put an end to that. Through their cli, you can quickly set up a mobile application for both iOS and android devices using javascript and react. This post will walk you through setting up a basic todo-list application. It is important that you have some experience with React, React hooks, and es6 so I suggest checking out the following links if that isn't the case:
To-do list demo
Here's what we are building:
Along the way, we will learn how to use the expo react native platform to set up our development environment and provide us with some starter code. We will also cover:
- Build-in React Native components
- Building a custom component
- Basic Styling
- Event Handling
- Passing props to components
Getting started
To build our todo-list application, we will be using expo. It is a well documented platform that performs much like create-react-app. In addition, it allows us to test out our application using our own mobile device or emulators through xCode or Android Studio. For this post, I will run the application on my own mobile device as I don't want to force anyone to waste an hour downloading emulators (though this is recommended if you want to develop a larger application). Let's first set up expo and download our starter code:
- Make sure you have node.js installed on your computer. You can download it here.
- Download the expo app from the apple or google play store. We will use this in a moment to see our application in action!
- Download the expo cli using
npm install expo-cli --global
in your terminal. - Run
expo init todo-demo
(todo-demo will be the name of our project's directory -- feel free to use any name you please). - running this command will prompt you to make a few choices.
- Under Managed Workflows select blank for your template.
- Give your app a name (can be whatever you like). Once again, I use
todo-demo
for my app's name.
-
cd todo-demo
and open the contents of the directory in the text editor of your choice! - Run
npm start
to run the application. You will see a qr-code in the terminal and also, a tab should open automatically in your browser with the same qr-code and some more information about the build. Use your iphone or android camera to scan the code. You should be prompted to open up the application in expo. Once open, you may be greeted with a welcome screen if you are first opening expo, but you should see the following once the app is loaded:
Components in React Native
Let's open up App.js
in our code editor and check out the contents. React Native is built on top of React
thus we must import react into every component that we make. Unlike React
, React Native
comes with only a few components built in. If you check out the documentation, you will see only about 20 components that are compatible with both iOS and Android devices. Fortunately, these components are all we need to build powerful applications! Back to our App.js
file, we notice that the component is importing two of these components: View
and Text
. View
is essentially our div
tag in React Native. We can give it properties like style
and also events to make them interactive. Let's modify our App.js
to include an input and button component so our users can type in a todo and post it to the screen.
- Import
Button
andTextInput
fromreact-native
. - Add
<TextInput />
and<Button title="Add Todo" />
below theText
component that is already inApp.js
. - Upon saving, you should see the new button render on your phone! The TextInput will not be visible. We can give it styling by adding an inline style prop. Add
style={{borderWidth: 1, width: 300}}
to theTextInput
component. Now, you should see the input field when you save!
Here is what my App.js
component looks like at this point:
export default function App() {
return (
<View style={styles.container}>
<Text>Awesome Todo Demo</Text>
<TextInput style={{borderWidth: 1, width: 300}}/>
<Button title="Add Todo"/>
</View>
);
}
Adding Events
Clicking on our button will trigger a nice animation, but clearly, nothing happens. Just like in react, we need to tell the Button component what to do when it is pressed. This is done with an onPress
prop. We could use an inline function to handle the button press, but it is best practice to create a separate function within our component to do this. We also need to add a prop to our TextInput component in order to save the input that is typed in. We will store the current input text and submitted todo's using the useState
hook built into React.
- Add state to our App component to store user text input and submitted todo's.
- import
useState
from react at top of our file. - create a state variable and setter for user input and submitted todos's. Place these before the return statement inside of your
App
component:
- import
const [textInput, setTextInput] = useState('');
const [todos, setTodos] = useState([]);
Notice we are initializing our textInput
state as an empty string and todos
as an array literal
- Create a
pressHandler
function above thereturn
inside of ourApp
component.
const pressHandler = () => {
setTodos([textInput, ...todos]);
};
We use the spread operator to extract all of the previously saved todos and add the new todo stored in textInput
to the end of the todos array.
- Create a
typingHandler
function to update thetextInput
state when the user types into the text input component:
const typingHandler = (value) => {
setTextInput(value);
}
- Add props to our
TextInput
andButton
components to fire these functions whenever text is inputed or the button is pressed. - Add
onChangeText={typingHandler} value={textInput}
props to theTextInput
component. - Add
onPress={pressHandler}
to theButton
component. We add thevalue
prop to ourTextInput
in order to store the current value that has been typed into the input area. It is automatically sent to ourtypingHandler
function whenever text is added.
Here is what our App.js
looks like so far:
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Button
} from 'react-native';
export default function App() {
const [textInput, setTextInput] = useState('');
const [todos, setTodos] = useState([]);
const pressHandler = () => {
setTodos([textInput, ...todos]);
};
const typingHandler = (value) => {
setTextInput(value);
}
return (
<View style={styles.container}>
<Text>Awesome Todo Demo</Text>
<TextInput
onChangeText={typingHandler}
value={textInput}
style={{borderWidth: 1, width: 300}}
/>
<Button title="Add Todo"/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Creating our own Todo
component
In order to display our submitted todo's, we need to create a new component! Normally, we would create a new file to do this but for the sake of this tutorial, we can do so under our App component.
- Create a
Todo
component at the bottom of App.js:
const Todo = props => (
<View
style={{ backgroundColor: "#eaeaea", width: 300, margin: 5 }}>
<Text>{props.text}</Text>
</View>
);
- Import
FlatList
component from react. This will be used to display our list. This component will allow our saved todos to be rendered to the screen. It will also allow us to scroll if there are more todos than space allows. Scrolling would otherwise not be enabled. - Add
FlatList
component below our submit button
<FlatList
data={todos}
renderItem={todo => <Todo text={todo.item}/>}
/>
Notice how we pass our todos
prop to the data
prop within the FlatList
component. The renderItem
prop acts like map
in javascript and accepts a function that is called for each todo in the todos
array. Notice that all of the text for each todo
is located on the item
property. Confusing, yes, but it is where we have to point to to access our todo text.
- Add
marginTop: 50
to thecontainer
object inside ofstyles
. This is necessary because adding theFlatList
pushes all of our components to the top of the phone screen.
At this point, we should have a working App! Go ahead and add some todo's and see it in action!
You may notice some yellow warning messages at the bottom of your phone. These appear because we are not giving each todo
component a unique key. For now, just dismiss the messages but know that you should be passing a unique key to each component when you do this in the future. Since todo's would probably be stored in some sort of database, this key would usually be available.
Here is the final code for App.js
:
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Button,
FlatList
} from 'react-native';
export default function App() {
const [textInput, setTextInput] = useState('');
const [todos, setTodos] = useState([]);
const pressHandler = () => {
setTodos([textInput, ...todos]);
};
const typingHandler = (value) => {
setTextInput(value);
}
return (
<View style={styles.container}>
<Text>Awesome Todo Demo</Text>
<TextInput
onChangeText={typingHandler}
value={textInput}
style={{ borderWidth: 1, width: 300 }}
/>
<Button
onPress={pressHandler}
title="Add Todo"
/>
<FlatList
data={todos}
renderItem={todo => <Todo text={todo.item}/>}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginTop: 50,
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
const Todo = props => (
<View
style={{ backgroundColor: "#eaeaea", width: 300, margin: 5 }}>
<Text>{props.text}</Text>
</View>
);
Posted on November 25, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.