SpinSpire logo

Building a Non-Trivial React App

Overview:

  • Why React?
  • Setting up a React project environment
  • Basics of React
  • Building and nesting components
  • API and AJAX requests
  • Adding Twitter Bootstrap for styling

Why React?

For this tutorial we are going to build a simple React application that will allow us to search for an artist on Spotify and return the top result for the search and a list of related artists, along with images, genres, and a link. In a follow up article I will introduce Redux into our React application.

React has been getting a lot of positive attention for the past few years, and for good reason. Facebook’s Javascript framework has been picked up by several other big companies, including Netflix, Tesla, Yahoo!, and Airbnb.

React operates very differently than other Javascript frameworks like Angular and Vue.js. Instead of writing your Javascript and HTML in separate files and having to referencing elements using id selectors or Angulars’ ng- syntax, you will write them both together in one harmonious file. This makes it easier for writing and debugging code. It also leads to more predictable outcomes in our applications.

The following in an example of a React Component being created and displayed on screen.

class HelloWorld extends React.Component {
  render () {
    return(<div> Hello World & {this.props.name}</div>);
  }
}

ReactDOM.render(
 <HelloWorld name="Troy" />,
  document.getElementById('root')
)

The XML-style syntax used above is called JSX. It is optional for React, but it makes life easier when you get used to it. JSX does need to be compiled by Babel before it can be used by the browser. 

React’s speed comes from its usage of a virtual DOM. Rather than manipulating the existing DOM, which is slow, React creates a copy of the DOM called a virtual DOM and manipulates that instead. Then React compares the virtual DOM to the real DOM and only swaps out only the changed sections. 

Unlike Angular and Ember, which are full frameworks, React is only the View layer in the MVC framework. React’s is only concerned with creating reusable UI components. So for your complete application you’re going to need to learn more about the ecosystem around React and plugging React into either an existing application or setting up a backend for your application.

Before you can get started with React, you will need to have Node and npm installed.  I’m using version 7.5.0.

Setting up a React Environment 

When it comes to setting up a React environment there are a few different options and it’s up to you to decide which is the most comfortable.
One option is to use Facebook’s Create React App command line tool to build React apps with zero build configuration. This will give us access to React, Webpack, Babel, and ESLint.This is great for beginners as it makes setting up an environment super easy. 

The other option is to set up an Express+Webpack+Babel environment which you would have full control over. This is a good option if you want to know what’s going on under the hood. If you’d like a sample of this kind of environment here’s an environment I enjoy using. For the purposes of this project I’ll be using this method. If you’d like learn about building an environment, here’s a tutorial I wrote just for that purpose.

Note: if you wish to use the react-create-app simply follow the below instructions.
In the console enter the following commands

npm install -g create-react-app
create-react-app react-artist-search

Run npm start in the console and your browser will automatically open on http://localhost:3000

Delete all the files from inside the src/ directory except for index.js.

If you already have an environment ready, the only libraries needed will be react, react-dom, and the libraries for Babel 

The Basics of React

As mentioned earlier, React allows you to create reusable UI components. The individual components should be self containing, meaning they contain all of the HTML and JS that is needed for the component. The component will also manage its own state or the props (properties) passed to the children. The state of the component is the data that is being managed by or for that component. The state should only be set inside of the component. props are set by the parent and passed to the children components and are fixed throughout the lifetime of the component. We will see how state and props work as we build our application.

Since we’re thinking in terms of components, let’s break down our application in its different components.

App - This is the holder for all of the rest of our components. It will also be the one that holds the state and provides the props to be passed to the children components. App for now will be index.js
Search - This will be our search bar component that will allow us to search for an artist.
Artist - The Artist component will be used in two different ways. It will be used to both show the number one search result and it will be used to display the individual related artists.
RelatedArtist - This will be the list component that will list out all of the related artists using the artist component.

Let’s set up the index.html file so that we can plug in our application. 
 

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>React Artist Search</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="bundle.js"></script>
  </body>
</html>

The bundle.js is being created webpack. The div with id=”root” is where our application will live.

Inside src/index.js we’ll create our App component.
 

import React from 'react'
import ReactDOM from 'react-dom'

class App extends React.Component {
    render() {
        return(
            <div>
                <p>Hello World!</p>
            </div>

        );
    }
}

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

After running npm start, you will see a friendly Hello World! Message.
The above code might look a little different than what you’re used to for JavaScript, that’s because it is written in ECMAScript 2015 or ES6. The import statements are like require statements. The two libraries we need right now are React and ReactDOM.

The App component is being declared by the class keyword, similar to creating classes in Java. It is also extending from React’s component. It isn’t a requirement to write out class like this, the class constructor is just syntactictical sugar for creating JavaScript classes.

Inside of the React class is our render function. render() is required for all React components. The code inside of the render function is written in JSX as mentioned earlier, this is optional but it does make writing React code much easier. As you might have noticed the className. This is one of the many differences in attribute naming, class is not used because it is a reserved word in JavaScript. Check out the list of attribute name differences here 
Also note that there is only one root element inside of the render function. React can only return that one root element and if there were more than one there would be an error.

The last bit of code is what will render our component to the screen to the div with the id=”root”.

Building and nesting components

Now we can create a new component for searching for an artist. 
Create a components directory under src and in this new directory create a file called Search.js

import React from 'react'

class Search extends React.Component {
    render() {
        return(
            <div className="search">
                <span>Search</span>
                <input />
            </div>

        );
    }
}

export default Search

The only real difference from our App component is the export default Search. This is similar to module.export functionality of ES5. This makes our Search component available to be imported by the rest of our application.

Now back in src/index.js we can import the Search component and place it inside of our root div.

import React from 'react'
import ReactDOM from 'react-dom'
import Search from './components/Search'

class App extends React.Component {
    render() {
        return(
            <div className="greeting">
                <p>Hello World!</p>
                <Search />
            </div>

        );
    }
}

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

Now you’ll see the Hello World message and the Search component.

But this is a pretty useless search box, so let’s make it at least a little bit interactive and we’ll work on that actual Spotify search functionality in a little bit. So to start let’s have the state start keeping track of what’s being entered into the input box and when the text changes the state will update with the value in the textbox.


In index.js we added a handler for when the Search component changes and passed that callback to the Search component. This is a time where the parent is passing a property to a child. This makes handleSearchSubmit available to the child element and the data can be passed up from the child to the parent.
 

import React from 'react'
import ReactDOM from 'react-dom'
import Search from './components/Search'

class App extends React.Component {

    handleSearchSubmit(term){
        console.log(term)
    }

    render() {
        return(
            <div className="artist-search">
                <Search onSearchSubmit={ this.handleSearchSubmit }/>
            </div>

        );
    }
}

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

In component/Search.js we added a constructor that takes in props initializes the state with a term that is an empty string. Then we added a handler for onTermChange which will be called on change of the input box. Inside of the onChange property we are calling the onTermChange and passing in the value of this input box via event.target.value. The syntax in which the function in between the curly braces is called a fat arrow function, which is a new way of writing functions in ES6. There is also now similar function for when the form is submitted called onTermSubmission. onTermSubmission takes just the event and preventDefault is applied to the event so that the page doesn’t reload. Then we set a let variable to this.state.term and then call onSearchSubmit with the term value. The reason I didn’t just call onSearchSubmit with this.state.term is because when calling that function this.state is not the this.state that we are expecting. 
 

import React from 'react'

class Search extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            term: ''
        }
    }
    onTermChange(term) {
        this.setState({term})
    }
    onTermSubmission(event) {
        event.preventDefault();
        let term = this.state.term
        this.props.onSearchSubmit(term)
    }
    render() {
        return(
            <div className="search">
                <form onSubmit={event => this.onTermSubmission(event)}>
                    <span>Search: </span>
                    <input onChange={event => this.onTermChange(event.target.value)} />
                </form>
            </div>

        );
    }
}

export default Search

*A quick word about the state. The state of our application is meant to be immutable. Meaning the state should be changed using the setState method and not via this.state directly. The only time one should use this.state is in the constructor initializing the state.

The passing of the property of onSearchSubmit with the handleSearchSubmit method as an argument is tricky at first and the key to this is remembering that now from Search we can call the onSearchSubmit method and that will trigger the parent’s (App) handleSearchSubmit method.

Now when you submit the form you should get a console log message with the value that was submitted.

It’s time for us to start building our Artist and RelatedArtist components. The RelatedArtist component will map out the array of the related Artist to our searched artist. To start, we’re just going to use some fake data rather than worrying about making a call to the Spotify API. Inside of src/index.js we are going to initialize a state with a fake searched artist and an array of related artists.
 

constructor(props) {
        super(props)
        this.state = {
            searchedArtist: {
                    name: 'My Band 1',
                    genres: ['genre 1','genre 2','genre 3'],
                    image: 'http://fakeimg.pl/250x100/'
            },
            relatedArtists: [
                {
                    name: 'Fake Band 1',
                    genres: ['genre 1','genre 2','genre 3'],
                    image: 'http://fakeimg.pl/250x100/'
                },
                {
                    name: 'Fake Band 2',
                    genres: ['genre 1','genre 2','genre 3'],
                    image: 'http://fakeimg.pl/250x100/'
                },
                {
                    name: 'Fake Band 3',
                    genres: ['genre 1','genre 2','genre 3'],
                    image: 'http://fakeimg.pl/250x100/'
                }
            ]
        }
    }

Now we can create our individual artist component which will be used to represent the searched artist and as the individuals in the RelatedArtists component.

In src/components/ create a new file called Artist.js
 

import React from 'react'

let Artist = ({ artist }) => {
    return(
        <div>
            <a href={ artist.href }> <h2>{ artist.name }</h2></a>
                <img src={ artist.image } />}
                <ul>
                    {artist.genres.map(g =>
                        <li key={Math.random()}>{g}</li>
                    )}
                </ul>

        </div>
    )
}

export default Artist

This looks somewhat like our other components, but what is going on with const Artist = ({ artist }) => { ?!? In React this is called a stateless functional component. These types of components have no need to know about the current state of our application and all they need to do is display the data that has been passed to them. They are also referred to as presentational components. To read more about stateless functional components I recommend this article 

The Math.random() being used above is to prevent React from warning about an item in a mapped array not having a key. Generally you should try to use an actual id property for your element rather than a random key value

In src/components/ create a new file called RelatedArtists.js

import React from 'react'
import Artist from './Artist'

const RelatedArtists = ({ relatedArtists }) => {
        return(
            <div>
                <div>
                    {relatedArtists.map( artist =>
                        <Artist key={Math.random()} artist={ artist } />)
                    }
                </div>
            </div>
        )
}

export default RelatedArtists;

RelatedArtists is simply taking in the related artists passed in from index.js and mapping them to instances of Artist.

Now when we visit http://localhost:3000/ we should see the following the list of Fake artists.

Let’s start to get some real data into our application using the Spotify API.
 

APIs and AJAX requests

First thing we’ll need is a library for making AJAX request. There are quite a few out there including isomorphic-fetch, axios, superagent and more. For this project we’re going to use axios.
Install axios with 
 

npm install axios --save

Then import fetch in index.js

import axios from ‘axios’

We’re going to need to make two separate calls to the Spotify API, one for getting the artist’s id and a second to search for the related artist 

We can clear out the fake data we entered in the App component’s state, but after doing so we’ll get an error about trying to map an unknown property. To remedy this issue I added a new property to the state called ready and initialized it to false. This make the this.state in the constructor look like this:
 

this.state = {
            ready: false,
            searchedArtist: '',
            relatedArtists: []
        }

Now we can use this property and a little bit of logic to prevent React from trying to render the searchedArtist and the RelatedArtists. Alternatively you could check if relatedArtists and searchedArtists were empty.
 

  {!this.state.ready ? <span>Search for something</span> :
                 <div>
                    <div >
                        <Artist artist={this.state.searchedArtist}/>
                    </div>
                    <div className="row">
                        <div>
                            <RelatedArtists relatedArtists={this.state.relatedArtists} />
                        </div>
                    </div>
                </div>
                }

The handleSearchSubmit is going to change quite a bit now. So I’m going to go over the changes piece by piece.

handleSearchSubmit(term){
       axios.get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
       .then((result) => {
            const searchedArtist = result.data.artists.items[0]
            this.setState({'searchedArtist': searchedArtist})
       })

In this section we are making our first axios.get request to get the artist name related to the term that was submitted in the Search component. Axios is a promise   based HTTP client which means we can write asynchronous . In this case, when the promise is resolved we will set the state with the top result of the searched term. After that has be another result for the related artists.

.then((result) => {
            axios.get(`https://api.spotify.com/v1/artists/${ this.state.searchedArtist.id }/related-artists`)
            .then((results) => {
            const relatedArtists = results.data.artists
            this.setState({'relatedArtists': relatedArtists})
        })

So then after the first request has completed we can fire the next get request for the related artists based on the searched artist’s id. Then when that has been resolved we can use the results of that request to set the state’s related artists. Now we just need to signal to the application we are ready to render.

.then(() => {
            this.setState({'ready': true})
        })

And it’s as simple as that. The fat arrow function being used () => is keeping the context of this so that we may use the correct this.state. This was also being used in the above two then statements as well.

Now that we have our results, we can add a last small piece to the Artist component, the top image if they have one at all. We’ll do another conditional check on the array of images, and if the artist has any we’ll present the top one, otherwise show some text. The image will also have some inline style to resize it.

const Artist = ({ artist }) => {
    return(
        <div>
            <a href={ artist.href }> <h2>{ artist.name }</h2></a>
                {!artist.images.length ? <p>No Image Available</p> : <img src={ artist.images[0].url } style={{ 
                    height: 100, width: 100
                }} />}
                <ul>
                    {artist.genres.map(g =>
                        <li key={Math.random()}>{g}</li>
                    )}
                </ul>

        </div>
    )
}

 

Adding Twitter Bootstrap for styling

First thing we need to do is download all the remaining libraries.

npm install bootstrap react-bootstrap --save
npm install css-loader style-loader file-loader url-loader --save-dev

Then in webpack.config.dev add the following in the module section
 

module: {
    // loaders allow for preprocessing of files
    loaders: [
      { test: /\.js$/, exclude: /node_modules/, loaders: ['babel-loader'] }, // preprocessing the .js files with babel-loader
       // Used for Bootstrap Less Source Files
      { test: /\.less/, loader: 'style-loader!css!less-loader' },
      // Used for Bootstrap Less Source Files
      { test: /\.css/, loader: 'style-loader!css-loader' },
      // Used for Bootstrap Glyphicon Fonts
      { test: /\.(woff2|woff|ttf|svg|eot)$/, loader: 'file-loader' }
    ]
  }

Then in src/index.js add the following

import Bootstrap from 'bootstrap/dist/css/bootstrap.css' 

and you are ready to use Bootstrap classes.

Now in the App component’s render method we can start adding some Bootstrap classes to the divs around the components. I’d like for the Search bar and SearchedArtist components centered and the RelatedArtists to be in a row of three.

<div className="react-artist-search container">
                <div className="row">
                    <Search onSearchSubmit={ this.handleSearchSubmit.bind(this) }/>
                </div>
                {!this.state.ready ? <div className="row"><span className="col-lg-6 col-lg-push-3">Search for something</span></div> :
                 <div>
                    <div className="row">
                        <div className="col-lg-6 col-lg-push-3">
                            <h3>Searched Artist</h3>
                            <Artist artist={this.state.searchedArtist} />
                        </div>
                    </div>
                    <div className="row">
                        <h4>Related Artist</h4>
                        <RelatedArtists relatedArtists={this.state.relatedArtists} />
                    </div>
                </div>
                }
            </div>

The tricky part will be for the RelatedArtists to be split into rows of three. The reason being is that we need to chunk the array of Artist results into groups of threes but the current RelatedArtists component renders all the results as one set. To accomplish this chunking method we can use the lodash ‘_’ library, which conveniently has a function called chunk. Fortunately it is possible to provide the map() function with the parameter of a function which can make the decision on how to render the mapped item, or in this case a set of items.

npm install lodash --save

Add 

import _ from ‘lodash’

to RelatedArtists.js.

We start by adding

let groups = _.chunk(relatedArtists, 4);

To chunk the array of objects into fours. Now with groups each mapped into another function which will wrap the group into a row.

 return(
        <div>
            {groups.map(renderRow)}
        </div>
    )

And the renderRow function will look like this: 

function renderRow(row) {
        return(
            <div className="row">
                { row.map(renderItem) } 
            </div>
        )
    }

And of course each of the items are Artist components which are wrapped with a col-sm-3. The renderItem function looks like this.
 

function renderItem(item) {
        return(
            <div className="col-sm-3">
                <Artist artist={item} />
            </div>
        )
    }

Now the app is somewhat presentable!

The above code is located here. Use the ‘react-only’ branch for this tutorial.