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 --globalin 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-demofor my app's name.
-
cd todo-demoand open the contents of the directory in the text editor of your choice! - Run
npm startto 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
ButtonandTextInputfromreact-native. - Add
<TextInput />and<Button title="Add Todo" />below theTextcomponent 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 theTextInputcomponent. 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
useStatefrom 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
Appcomponent:
- 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
pressHandlerfunction above thereturninside of ourAppcomponent.
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
typingHandlerfunction to update thetextInputstate when the user types into the text input component:
const typingHandler = (value) => {
setTextInput(value);
}
- Add props to our
TextInputandButtoncomponents to fire these functions whenever text is inputed or the button is pressed. - Add
onChangeText={typingHandler} value={textInput}props to theTextInputcomponent. - Add
onPress={pressHandler}to theButtoncomponent. We add thevalueprop to ourTextInputin order to store the current value that has been typed into the input area. It is automatically sent to ourtypingHandlerfunction 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
Todocomponent at the bottom of App.js:
const Todo = props => (
<View
style={{ backgroundColor: "#eaeaea", width: 300, margin: 5 }}>
<Text>{props.text}</Text>
</View>
);
- Import
FlatListcomponent 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
FlatListcomponent 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: 50to thecontainerobject inside ofstyles. This is necessary because adding theFlatListpushes 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>
);
Top comments (0)