Let’s build a Venue Finder app using React and Redux. Why not?

Goals

So, what do we want from our Venue Finder? Well let’s have a think about the kind of features it could have!

We want to…

  • …Search for Venue’s in order of recommendations
  • …Search for type of venues, such as Restaurants, Bars etc
  • …Get Venue’s near you by getting your location!

Sounds pretty simple, but it could definitely be of a lot of use to people. 🙂

Here’s what we want to achieve technically…

  • Creating reusable, singular components with React
  • Scale an application with a component architecture
  • Use the Foursquare API to get data of venues based on locations.
  • Manage the state of the application using Redux

Alrightey, lets begin!

Set Up

No need to reinvent the wheel … Let’s initialise our React App by using Create React App. It gives us the necessary boiler plate package and configuration to get started nice and quick, including the capability to view our application on a running local server.

First we install the package globally to our machine:

$ npm install -g create-react-app

Then…

$ create-react-app venue-finder cd my-app/ npm start

Our app begins life in index.js, which gets added after the script is run. The job of `index.js` is to render the `<App />` component to the root element of the page using ‘ReactDOM’.

ReactDOM.render(<App />, document.getElementById('root'));

Index.js imports the App class, whose job is to render a JSX template. This is where our work on Venue Finder starts!

Grabbing The Data Using Fetch

The first thing we want to do is get our Foursquare venue data which, for now will happen on load. To make this easier, we can use a library called Fetch, a polyfill for Javascript that makes AJAX calls easier. I’d like to choose Fetch here because it is becoming a standard for Javascript.

For now we will add Fetch with yarn, and import it into our App component:

$ yarn add whatwg-fetch
import React, { Component } from 'react';
import logo from './logo.svg';
import 'whatwg-fetch';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React!</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

In order to call the Foursquare data when the app is initialised, we run a Fetch call inside a componentDidMount method, the perfect place to load the data. As you can see above, we have a bunch of default JSX in the template that came from Create React App. At the same time, we clean up the JSX:

import React, { Component } from 'react';
import 'whatwg-fetch';

class App extends Component {

  componentDidMount() {

    const venuesEndpoint = 'https://api.foursquare.com/v2/venues/explore?';

    const params = {
      client_id: CLIENT_ID, //Client ID obtained by getting developer access
      client_secret: CLIENT_SECRET, //Client Secret obtained by getting developer access
      limit: 100, //The max number of venues to load
      query: 'Pubs', //The type of venues we want to query
      v: '20130619', //The version of the API.
      ll: '51.5073,0.1276' //The latitude and longitude of Charing Cross, London
    };

    fetch(venuesEndpoint + new URLSearchParams(params), {
      method: 'GET'
    }).then();

  }
  render() {
    return (
      <div></div>
    );
  }
}
export default App;

In the above, we are using the Explore Venues API to find us Pubs near Charing Cross, London. By looking at the Foursquare API documentation, we know what parameters to pass in.

Note, Fetch does not currently allow fancy way to build query strings, however native to Javascript is the new URLSearchParams method. This allows more expressive ways to build query parameters using objects. This is available in Chrome 51+ and can be added to other browsers using polyfills (which are beyond the scope of this post).

Okay so we are loading the data now, but we aren’t loading to the page! Let’s update our React components state and render to the virtual DOM.

Update the Component State

We can update the components state using this.setState and updating in the JSX. The power of `setState` is that it detects changes in the virtual DOM, and automatically renders to the DOM if it detects any changes. We don’t need to call render() each time we get a change! .. And because it only changes the bits that are different, its it’s extremely efficient. This is one of the main advantages of using React.

First of all let’s define that we have a venues state in our component by building a constructor:

class App extends Component {

  constructor() {
    
    super();

    this.state = {
      venues: []
    };
  }

//...

}

In the above, we declare a default state of an empty array of venues. However before we do so, we must call super()  because ‘this’ is not initialised without it!  In ES6 a subclass can’t have a constructor without using Super().

Now, we update our fetch to handle a real response object, and hopefully we will get some venues! By reading the Foursquare docs, we know we can do something like this:

fetch(venuesEndpoint + new URLSearchParams(params), {
  method: 'GET'
}).then(response => response.json()).then(response => {
  this.setState({venues: response.response.groups[0].items}); //Set the components state
});

The key to updating our component here is this.setState. The response of the call updates the components state, so that we can later render the venues to the page.

But wait, it doesn’t work… why? Because the context of ‘this’ is lost in the in the Fetch response block, as it has it’s own context.

In order to update our React components state, we must “remember” it’s context. We can do this by binding the React components context to setState and saving it to a variable for later use:

var setVenueState = this.setState.bind(this);

Putting it all together (Note I have split the componentDidMount() method into a getVenues() method):

import React, { Component } from 'react';
import 'whatwg-fetch';

class App extends Component {

  constructor() {
    
    super();

    this.state = {
      venues: []
    };
    
    
  }

  componentDidMount() {
    this.getVenues();
  }

  getVenues() {

    let setVenueState = this.setState.bind(this);

    const venuesEndpoint = 'https://api.foursquare.com/v2/venues/explore?';

    const params = {
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      limit: 6,
      query: 'Pubs',
      v: '20130619',
      ll: '51.5073,0.1276'
    };

    fetch(venuesEndpoint + new URLSearchParams(params), {
      method: 'GET'
    }).then(response => response.json()).then(response => {
      setVenueState({venues: response.response.groups[0].items});
    });
  }

  render() {
    return (
      <div></div>
    );
  }
}

export default App;

Rendering to the Template

Now that we have updated our app’s state based on the response, it’s quite easy to update. Within the render function, we add:

render() {

  var venueList = this.state.venues.map(item =>
    <li>{item.venue.name}</li>
  );

  return (
    <ul>
      {venueList}
    </ul>
  );

}

In the above, venueList returns an an array of JSX elements. Because venueList results in valid JSX, we can render in return function using the curly brace syntax. Checking in the browser we get our list of venues!

Splitting up the Venue Component

Currently, all of our logic and rendering is in a single file: app.js. This is no good if we want to make our components reusable, and our application scalable in its entirety.

First, a bit of a clean up. Let’s delete the files that we got earlier from Create React App:

We then add Venue.js, and create a component that extends React component. We also export the component so it’s available to be loaded into App.js as a dependency.

Venue.js:

import React, { Component } from 'react';
import 'whatwg-fetch';

export class Venue extends Component { // Make sure we export!
  
  render() {

    return;

  }

}

Back in App.js we import the Venue class:

import { Venue } from './Venue';

In Venue.js we have the render function return some static JSX:

render() {

  return <li>I'm a venue!</li>

}

Update App.js to render the Venue component using the JSX syntax:

render() {

  var venueList = this.state.venues.map(item =>
    <Venue /> //The new Venue component
  );

  return (
    <ul>
      {venueList}
    </ul>
  );
}

Voila! The list returns ‘I’m a Venue’ for each venue (Not too great, because we lose functionality. But at least it demonstrates multiple components).

Venue as a Reusable Component

For each instance of Venue that get’s loaded, we want to pass in some data. This is the crux of making a reusable component. In AngularJs for example, this is known as a “directive”, where you have an instance of scope where you can pass in data.

In React, it’s pretty simple. We use this.props and pass the data through as an attribute.

In app.js we create a name attribute on the venue and pass in a property from the map as a value. We also need to make sure we add a key.

render() {

  var venueList = this.state.venues.map((item,i) =>
    <Venue key={i} name={item.venue.name}/> //Create a new "name attribute"
  );

  return (
    <ul>
      {venueList}
    </ul>
  );
}

In Venue.js this.props.name becomes available to us, allowing us to render the venue name that we pass through.

render() {

  return <li>{this.props.name}</li> //pass the name through

}

Our venue’s display again 🙂

Refactoring Venue as a Stateless Component

React 14 introduced Functional Stateless Components, which means that if we have a simple component without logic, we can get rid of the classes, and the use of this and this.props.

Hence, It therefore becomes possible to write our Venue component with two lines of code (where one is the import!). Let’s refactor:

import React, { Component } from 'react';
export const Venue = ({ name }) => <li>{name}</li>; //wow

Add Local Venue Functionality

It’s a bit boring always giving you venue’s in Charing Cross. How about recommended venue’s near your location. Here we pass in the user’s location to Foursquare using the HTML5 navigator api:

getLocation(callback) {

  navigator.geolocation.getCurrentPosition(function(location) {
    callback(location.coords.latitude + ',' + location.coords.longitude)
  })
}

getVenues() {

  let setVenueState = this.setState.bind(this);

  const venuesEndpoint = 'https://api.foursquare.com/v2/venues/explore?';

  this.getLocation(function (latlong) {

    const params = {
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      limit: 100,
      query: 'Pubs',
      v: '20130619',
      ll: latlong
    };

    fetch(venuesEndpoint + new URLSearchParams(params), {
      method: 'GET'
    }).then(response => response.json()).then(response => {
      setVenueState({venues: response.response.groups[0].items});
    });

  });

}

Wrapping Up – Adding Search Functionality

We nearly have a finished proof of concept! Right now, the app automatically searches for a user. How about we give the user the choice of what they what to look for? What about “Coffee”, “Kebabs”, or anything you like for your area?

We will begin by adding our Search component:

Search.js

import React, { Component } from 'react';

export class Search extends React.Component {

  constructor(props) {
    super(props);
    this.state = {value: ''};
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  handleSubmit(event) {
    event.preventDefault();
    this.props.onSubmit(this.state.value);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input id="venueType" onChange={this.handleChange} value={this.state.value} placeholder="search for venues" />
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

App.js

import { Search } from './Search';

and…

return (
  <div>
    <Search onSubmit={(value)=>this.handleSubmit(value)}/>
    <ul>
      {venueList}
    </ul>
  </div>
);

In the above, our new Search component in Search.js is added, and is imported into App.js exactly like we did with Venue.js.

Search is a bit more complicated however, as it needs its own state when the user enters text. It also takes a value from App.js that executes a prop function when the form is submitted.

So basically, what we are doing is when the user finishes typing and submits the form we call the function that is passed into Search from App.js

The function that we would like to call is here:

App.js

handleSubmit(query) {
  this.getVenues(query);
}

And now we update getVenues to take a parameter, making it wholly dynamic:

getVenues(query) {

  let setVenueState = this.setState.bind(this);

  const venuesEndpoint = 'https://api.foursquare.com/v2/venues/explore?';

  this.getLocation(function (latlong) {

    const params = {
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      limit: 100,
      query: query,
      v: '20130619',
      ll: latlong
    };

    fetch(venuesEndpoint + new URLSearchParams(params), {
      method: 'GET'
    }).then(response => response.json()).then(response => {
      setVenueState({venues: response.response.groups[0].items});
    });

  });

}

…and we’ve built our search functionality!

Oh and let’s not forget that now we have made getVenues dynamic, when it gets called on page load we could default to ‘Pubs’ like we were before.

componentDidMount() {
  this.getVenues('Pubs');
}

Conclusion

This ends part one of building our Venues Suggestion app with React and the Foursquare API. Part two will use Redux to enhance the app even further. So what has been achieved here?

  • Setting up a react boiler plate with as minimal configuration as possible so that we get up to speed quickly
  • Rendering the app using ReactDOM.Render()
  • Using Fetch for asynchronous HTTP requests to the Foursquare API
  • Setting up React Components with a State
  • Add geolocation using the navigator api
  • Make our components reusable (Venue Component, Search Component)
  • Refactor the simple Venue component to be a stateless component with minimal code
  • Components can communicate with each other using this.props
  • Components execute functions in the parent and pass in parameters, such as when the form submits

Source Code on Github

You can view the source code for this article by clicking on this link. Feel free to make suggestions or fork etc 🙂

Click here to view the source code so far!