Building a React Native app w/ expo
Tags: javascript, react, reactnative, beginners
Published: 2019-11-25 03:07:36

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](https://expo.io/) 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](https://reactjs.org/tutorial/tutorial.html)
 2.  [React Hooks](https://reactjs.org/docs/hooks-intro.html)
 3.  [es6 syntax](http://es6-features.org/)

## To-do list demo
Here's what we are building:
{% youtube 9um-CPH4E_Q %}

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](https://nodejs.org/en/).
 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!
 2. Download the expo cli using `npm install expo-cli --global` in your terminal.
 3. 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.
 4. `cd todo-demo` and open the contents of the directory in the text editor of your choice!
 5. 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](https://thepracticaldev.s3.amazonaws.com/i/7jxu3ufjhh15jlzjhn3f.png)

## 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](https://facebook.github.io/react-native/docs/components-and-apis.html), 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:
```jsx
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:
```jsx
  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*
 2. Create a `pressHandler` function above the `return` inside of our `App` component.
```jsx
  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.*
 3. Create a `typingHandler` function to update the `textInput` state when the user types into the text input component:
```jsx
  const typingHandler = (value) => {
    setTextInput(value);
  }
```
 4. Add props to our `TextInput` and `Button` components to fire these functions whenever text is inputed or the button is pressed.
 - Add `onChangeText={typingHandler} value={textInput}` props to the `TextInput` component.
 - 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:
```jsx
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*:
```jsx
const Todo = props => (
  <View 
    style={{ backgroundColor: "#eaeaea", width: 300, margin: 5 }}>
    <Text>{props.text}</Text>
  </View>
);
```
 2. 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.
 3.  Add `FlatList` component below our submit button
```jsx
      <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.*
 4.  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](https://thepracticaldev.s3.amazonaws.com/i/og1pebsfon897m9d0mrr.png)

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`:
```jsx
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>
);
```