DEV Community

Cover image for Building a React Native app w/ expo
Avery Berkowitz
Avery Berkowitz

Posted on

Building a React Native app w/ expo

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:

  1. React
  2. React Hooks
  3. es6 syntax

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:

  1. Make sure you have node.js installed on your computer. You can download it here.
  2. Download the expo app from the apple or google play store. We will use this in a moment to see our application in action!
  3. Download the expo cli using npm install expo-cli --global in your terminal.
  4. Run expo init todo-demo (todo-demo will be the name of our project's directory -- feel free to use any name you please).
  5. 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.
  6. cd todo-demo and open the contents of the directory in the text editor of your choice!
  7. 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: iphone

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.

  1. Import Button and TextInput from react-native.
  2. Add <TextInput /> and <Button title="Add Todo" /> below the Text component that is already in App.js.
  3. 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 the TextInput 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.

  1. 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:
  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

  1. Create a pressHandler function above the return inside of our App 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.

  1. Create a typingHandler function to update the textInput state when the user types into the text input component:
  const typingHandler = (value) => {
    setTextInput(value);
  }
  1. Add props to our TextInput and Button components to fire these functions whenever text is inputed or the button is pressed.
  2. Add onChangeText={typingHandler} value={textInput} props to the TextInput component.
  3. Add onPress={pressHandler} to the Button component. We add the value prop to our TextInput in order to store the current value that has been typed into the input area. It is automatically sent to our typingHandler 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.

  1. 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>
);
  1. 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.
  2. 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.

  1. Add marginTop: 50 to the container object inside of styles. This is necessary because adding the FlatList 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!
Alt Text

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)