Building a Coffee Map with React Native
Tags: reactnative, react, javascript, mobile
Published: 2018-02-15 07:34:14

---
title: Building a Coffee Map with React Native
published: true
description: Learn to scaffold a simple React Native application using Expo and touch on using it's location and map APIs.
tags: reactnative,react,javascript,mobile
---

The young web developer knows the web. They have spent countless hours slinging divs and casting margins. They have hammered out countless to-do applications in JavaScript, Python, Golang, Fortran, Basic... you name it! 

But now, this hotshot developer wants to conquer a less familiar territory. They want to displace their teams' gang of mobile developers; all of them, by using the hip new framework, React Native. Thinking it'll be practically like writing a simple web application, they install the React Native CLI and scaffold an empty project.

It starts off just fine. They find out that instead of divs, they must create Views. In order to create text, they must use the built in Text component. Instead of CSS, they must use inline JavaScript styles. In order to create a nice layout, they require some knowledge of flexbox. 

But then they want to wield more powerful features like geolocation, audio input, or push notifications. They find that in order to enable these features, they must open up XCode and edit some fairly verbose configuration files, change the plist,  and create headers and implementations in Objective-C. They find that maybe they should stick to the web.

# Enter Expo

Fortunately, the beautiful team over at [Expo](https://expo.io) has created a pretty powerful SDK that greatly improves the React Native developer experience. They have made it such that when you create an application with Expo, you will probably never have to crack open XCode or edit any platform specific configuration files. 

If you're familiar with create-react-app for bootstrapping a React web application, Expo works in pretty much the same way. You run `exp init <project-name>` from the command line and then just enter the project directory and run it with `exp start`. Expo provides you with a QR code that you can use to view your project right on your device. You could also just run the simulator using `exp ios` or `exp android`. The simulator is a little bit faster between saves but doesn't have quite the performance as the real device. 

#Espressopedia

Its like expedia for coffee. Or something like that. From a high level standpoint, the app will go like this:

* we will have a map view with the user's location in center
* on the map will be a set of markers for the locations of coffee & tea shops nearby

We will use the Yelp API for getting the list of coffee places. Their API is pretty straightforward to setup and use, just head over to [Yelp](https://www.yelp.com/developers/v3/manage_app) and sign up then create an app.


##Creating a new project

Lets get coding. Start by installing the expo cli.

```
npm install -g exp
```

Then run
```
exp init espressopedia
```

It'll ask you whether you want to set up a blank template project or one with some starter files like a tab navigator. I chose the blank project because we wont need any tab navigation.

Now I'm going to test the app in the iOS simulator. You can use your own device as well but then its up to you to download the expo client and set it up. To run the simulator:

```
exp ios

# or for Android

exp android
```

and to build the project

```
exp start
```

Now when you open up your root directory you will find the blank template `App.js` file.

```javascript
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Open up App.js to start working on your app!</Text>
      </View>
    );
  }
}

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

```

If you're a React veteran, this file shouldn't look too intimidating. Note the usage of `View` and `Text` tags. This file uses `StyleSheet` but we could've defined styles as a plain object as well.

##Building the Map

The first expo API we'll explore is the `MapView` component. 

```javascript
// app/components/Map.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { MapView } from 'expo';

const Marker = MapView.Marker;

export default class Map extends Component {
  renderMarkers() {
    return this.props.places.map((place, i) => (
      <Marker 
        key={i}
        title={place.name}
        coordinate={place.coords}
      />
    ));
  }

  render() {
    const { region } = this.props

    return (
      <MapView
	  style={styles.container}
	  region={region}
	  showsUserLocation
	  showsMyLocationButton
      >
        {this.renderMarkers()}
      </MapView>
    );
  }
}

const styles = {
  container: {
    width: '100%',
    height: '80%'
  }
}
```

This Map component is a wrapper for Expo's `MapView` component. By making electing to wrap the built-in component we can decorate our map with functionality through lifecycle methods and application-specific methods, such as rendering the markers. Here, it is not implemented specific to our use case of finding coffee shops -- that decision is made in the `App.js` component that renders it.

```javascript
// App.js
import React from 'react';
import { Text, SafeAreaView } from 'react-native';
import Map from './app/components/Map'

// A placeholder until we get our own location
const region = {
  latitude: 37.321996988,
  longitude: -122.0325472123455,
  latitudeDelta: 0.0922,
  longitudeDelta: 0.0421
}

export default class App extends React.Component {
  state = {
    region: null
    coffeeShops: []
  }

  render() {
    return (
      <SafeAreaView style={styles.container}>
        <Map
          region={region}
          places={this.state.coffeeShops}
        />
      </SafeAreaView>
    );
  }
}
```

Here we pass down an initial region object which should put your map somewhere around the city of Cupertino. We'll replace this when we get user location to center our map view. We also use `SafeAreaView` for the top level component. This allows our content to look good even with the iPhone X's whacky screen region.

# Getting User Location

To get the user location, we can use the `Location` and `Permissions` modules within expo. Add this to `App.js` 

```javascript
// App.js
/* ... */
import { Location, Permissions } from 'expo'

const deltas = {
  latitudeDelta: 0.0922,
  longitudeDelta: 0.0421
};

export default App extends Component {
  state = {
    region: null,
    coffeeShops: []
  };
  
  componentWillMount() {
    this.getLocationAsync();
  }

  getLocationAsync = async () => {
    let { status } = await Permissions.askAsync(Permissions.LOCATION);
    if (status !== 'granted') {
      this.setState({
        errorMessage: 'Permission to access location was denied'
      });
    }

    let location = await Location.getCurrentPositionAsync({});
    const region = {
      latitude: location.coords.latitude,
      longitude: location.coords.longitude,
      ...deltas
    };
    await this.setState({ region });
  }

    render() { /* ... */ }
}

```

Here we make sure to get user's permission to use geolocation as our App is mounting. If they refuse, we set an errorMessage in state and have the option of display that rather than the map. Once permission is granted, we can call `getCurrentPositionAsync` which returns a location object that is a little more complex than we need, so we massage it getting only the properties we want, namely latitude and longitude (and the deltas so our map knows how much to zoom).

# Fetching data

To get our coffee shop data, we'll query the Yelp API. Its pretty easy to setup an app on Yelp, just log in and go to [Manage App](https://www.yelp.com/developers/v3/manage_app). Here you'll get an API key which you can use to consume their API.

Just like on web, we can leverage the axios library to perform HTTP requests. Go ahead and run

```
npm install --save axios
```

Now for the sake of modularity, I will add a new folder called `services` inside the app directory, and inside this folder, a file called `yelp.js`. Here we defined how our application will interface with Yelp's API.

```javascript
// app/services/yelp.js
import axios from 'axios';

const YELP_API_KEY = '<YOUR_API_KEY>';

const api = axios.create({
  baseURL: 'https://api.yelp.com/v3',
  headers: {
    Authorization: `Bearer ${YELP_API_KEY}`
  }
});

const getCoffeeShops = userLocation => {
  return api
    .get('/businesses/search', {
      params: {
        limit: 10,
        categories: 'coffee,coffeeroasteries,coffeeshops',
        ...userLocation
      }
    })
    .then(res =>
      res.data.businesses.map(business => {
        return {
          name: business.name,
          coords: business.coordinates
        };
      })
    )
    .catch(error => console.error(error));
};

export default {
  getCoffeeShops
};
```
This service works by creating an http client with `axios.create` and passing in the `baseURL` and the `Authorization` header. We can then use it to query the Yelp api by sending a GET request to `https://api.yelp.com/v3/businesses/search` with query parameters. Axios makes this easier by allowing us to set the parameters as an object in its argument list. After that, the only part of this `getCoffeeShops` method that makes it unique to our app is where we specify `categories` in the request. We could change that to "vegan" or "burgers" and it would change the results of our map completely. Well mostly.

Now let us consume this service inside `App.js`, start by importing YelpService.

```jsx
// App.js
/* ... */
import YelpService from './app/services/yelp'

export default App extends Component {
  
  /* ... */

  getCoffeeShops = async () => {
    const { latitude, longitude } = this.state.region;
    const userLocation = { latitude, longitude };
    const coffeeShops = await YelpService.getCoffeeShops(userLocation);
    this.setState({ coffeeShops });
  };

  getLocationAsync = async () => {

    /* ... */

    // Add this line!
    await this.getCoffeeShops();
  }

  render() {
    const { region, coffeeShops } = this.state;
    return (
      <SafeAreaView style={styles.container}>
        <Map region={region} places={coffeeShops} />
      </SafeAreaView>
    );
  }
}
```

Now we're in business! You should be able to see a map with markers on it, as well as your location. Unless you're on a simulator. Then you'll see that you're in San Francisco somewhere. I wonder if that is where the Expo team works?

![Final](https://thepracticaldev.s3.amazonaws.com/i/f0mdowhjji8m8jnmjf9d.png)

I hope you got a kick out of this article somehow, hopefully it'll inspire you to make something even cooler. During my prep for this article I created a similar app with a few more bells and whistles in that it even has filter buttons. One of the filters is "Starbucks", you know in case you couldn't find them all. If you're interested, you can see that code [here](https://github.com/iwilsonq/espressopedia).

Since this is my first post, I'd appreciate comments, suggestions, or critiques. That will fire up engagement and make sure that future posts are even better. 

Until next time.