Rock and Roll React.js Tutorial – Part 1

In my GitHub Trending Repos tutorial I created a simple 1-page app. It introduced Redux to store state and Redux-Thunk to perform asynchronous calls. In the scheme of things, I don’t think it can be classed as anything but “simple”. Time to step it up with Rock and Roll React.js Tutorial – Part 1.

To stretch myself, I wanted an app that:

With huge thanks to Balint Erdi for giving me permission. I think I found the perfect app to base this tutorial on.

What are we going to build?

We’re going to build a React.js version of the Rock and Roll App built in Balint’s Ember.js tutorial https://www.toptal.com/javascript/a-step-by-step-guide-to-building-your-first-ember-js-app.

Rock and Roll - Finished App

Rock and Roll – Finished App

Disclaimer

I’m not an ember developer. I can’t provide a “how to move to React from Ember guide”. Why bother? I’m aiming this at two sets of people.

Group 1 – Ember Developers curious about React

If you know Ember and want to learn React, I hope this will be helpful. If it’s not, but you think it could with some changes. Then please get in touch to help me make better.

Group 2 – New React Developers trying to grow their knowledge

If you’re learning React, I think this sits in the “simple to medium” range. It’s a nice app to learn a bit of react-router, a simple create operation and build a more complex UI. As we’re basing it on a design, it’s also a good way to practice breaking a UI down into components. I’m not an expert, so please get in touch to help me make it better.

I wrote my last tutorial after I completed the app. This time, I’m going capture my initial design and thought process before coding begins. This will help show how these changed over time. It may prove to be a bad idea, but I’ve never tried it before and think it might be of some use. Let me know below if you disagree.

Component Breakdown

The final version of the Ember app is available at https://rarwe-demo.pagefrontapp.com/bands/. It doesn’t appear to be identical to the tutorial, but that doesn’t matter for us. We’re just trying to re-create it in React.

Have a play with the demo and you will see it’s made up of 2 parts:

  1. Band List (left-hand side)
  2. Song List (right-hand side)

When working with React, it’s a good idea to build small components. Each component should try to do as little as possible, ideally one thing. You should then be able to re-use the components in other apps with zero changes. Let’s try that now.

Initial App Load

I’ve decided to breakdown the app when it’s loaded into these components.

Rock and Roll Part 1 - Component Breakdown 1

Rock and Roll Part 1 – Component Breakdown 1

Green boxes represent “top-level” components. Orange boxes are components inside these “top-level” components or other orange components. I’ve decided to break down the app into:

  • “Header” top-level component (top green one)
  • “BandsList” top-level component (left green one)
    • “NewBand” component (top orange one)
    • “BandRow” component (bottom orange one)
  • “SongList” top-level component (right green one)

Selecting a Band

By clicking on a Band, both the “BandRow” component and the “SongList” component change. I’m going to break the “SongList” down further.

Rock and Roll Part 1 - Component Breakdown 1

Rock and Roll Part 1 – Component Breakdown 1

  • “NewSong” component (top orange one)
  • “SongRow” component (second orange one)
  • “StarRating” component (tiny orange one surrounding the stars)

I could break “BandRow” down a little more. I could contain a “SelectedArrow” component, but that’s taking the decomposition too far.

Routes

There are other routers available in React, but by far the most popular is React-Router.

There’s a lot written about React-Router. My favourite (so far) is this nice and slow explanation https://www.kirupa.com/react/creating_single_page_app_react_using_react_router.htm. If you prefer videos, I’d also recommend Dan Abramov’s second Egghead.io videos https://egghead.io/courses/building-react-applications-with-idiomatic-redux.

One of the aims for this tutorial was to build a multi-page application. At the time, I thought all URL changes meant a new page. With the help of the above link, I realised that’s not actually true.

JavaScript routers use an “app-frame”. If you’re like me and come from a .NET background, think master or layout page (depending on how old you are).

The Rock and Roll app has two routes:

  1. /bands – the URL when the app is first loaded
  2. /bands//songs – the URL when selecting a band

The react-router configuration for this can be:

    <Router history={browserHistory}>
        <Route path="/" component={App}>
            <IndexRedirect to="bands" />
            <Route path="bands(/:bandid/songs)" components={{ bands: BandsList, songs: SongList }} />
        </Route>
    </Router>

Line 1 – The router will use browserHistory. This prevents # and a strange number appearing in our URL

Line 2 – Our first route and is the root of the domain. It will cause the <App /> component to render. This will be our app-frame (see above).

Line 3 – Like the Ember app, we want http://domain to redirect to http://domain/bands. <IndexRedirect /> does just that.

Line 4 – We take advantage of optional URL pattern matching. That’s the bit inside the brackets, to define just 1 child route. This pattern will match both /bands and bands/4/songs. React-Router will pass as props a params object that contains bandid with a value of 4 if needed.

Also on line 4, we define two components, <BandsList /> and <SongList />. These are called bands and songs respectively. They will be passed into <App > as props. We can render them with {this.props.bands}.

Let’s get coding

With the introduction and initial design looking good, let’s get coding.

I’ll be using Cory House’s react-slingshot again. Please let me know if you follow along and something isn’t working.

After clearing out the demo with npm run remove-demo we need to do some setup. First, update src\index.ejs to include bootstrap, i.e. add a <link> tag just after the <title> tag. Second, update ‘src\reducers\index.js` to:

import { combineReducers } from 'redux';
export default combineReducers => { return;}

I think that makes our root reducer return nothing. As we’re not concerned with Redux just yet and it makes the app work, it’ll do for now!

Configure React-Router

You will see some project store the router configuration in a separate file. For this app I’m going to put it in the entry point. Update src\index.js to contain our route configuration:

/* eslint-disable import/default */

import React from 'react';
import { render } from 'react-dom';
import  App  from './components/App';
import  BandsList  from './components/BandsList';
import  SongsList  from './components/SongsList';
import { Provider } from 'react-redux';
import { Router, Route, browserHistory, IndexRedirect } from 'react-router';
import configureStore from './store/configureStore';

const store = configureStore();

render(
  <Provider store={store}>
    <Router history={browserHistory}>
        <Route path="/" component={App}>
            <IndexRedirect to="bands" />
            <Route path="bands(/:bandid/songs)" components={{ bands: BandsList, songs: SongsList }} />
        </Route>
    </Router>
  </Provider>,
  document.getElementById('app')
);

That’s identical to above. But this time, we’ve wrapped the <Route /> with the Redux <Provider /> component.

To see it working, let’s create the 3 components we’re using here. Starting with our App-Frame by creating a file called App.js in src\components\:

import React, {Component, PropTypes} from 'react';

let headerStyles = {
    paddingBottom: "9px",
    margin: "40px 0 20px",
    borderBottom: "1px solid #eee",
    color: "#555"
}

const App = ({ bands, songs }) => (
    <div className="container">
        <div className="row" style={headerStyles}>
            <h1>Rock &amp; Roll React</h1>
        </div>
        <div className="row">
            <div className="col-md-4">
                {bands}
            </div>
            <div className="col-md-8">
                {songs}
            </div>
        </div>
    </div>
);

export default App;

This will act as out App-Frame. I’ve “cheated” by loading the Ember.js app and taken the bootstrap styling, i.e. “container”, “col-md-4” etc.

Lines 3-8 define a variable called headerStyles. It’s used in inline-styles. I used to define styles in the app stylesheet the “traditional” way. I experimented with them here to see how they worked.

I can see the advantage of inline-styles for components that don’t need CSS updates. But I haven’t used them enough to say “THIS IS THE FUTURE”. I’ve left them in this tutorial so we can stay in React/JavaScript mode. Let me know if you think that’s a mistake.

You might also notice this component doesn’t extend Component. It’s a Stateless Functional Component. I recommend you read https://tylermcginnis.com/functional-components-vs-stateless-functional-components-vs-stateless-components-630fdfd90c9c#.3fjwkxzcd to see what it’s all about.

I’m choosing to think of them as a quicker way to create components. The don’t have access to the react lifecycle hooks. So when I don’t need the hooks, I try to use this syntax.

There’s talk of optimising React for these types of component in the future. I suspect there will be a codemod to convert between the two, so don’t worry about it for now if you don’t want to.

Let’s define two simple components for <BandsList /> and <SongsList />.

In src\components\BandsList.js:

import React, {Component, PropTypes} from 'react';

class BandsList extends Component {
    render() {
        return (
            <div>
                <p>BANDSLIST</p>
                {this.props.params.bandid || "No Bands Passed"}
            </div>
        );
    }
}

export default BandsList;

And in src\components\SongsList.js

import React, {Component, PropTypes} from 'react';
import {Link} from 'react-router';

class SongsList extends Component {
    render() {
        return (
            <div>
                SONGLIST
                <Link to="/bands/4/songs">Click Me</Link>
            </div>
        );
    }
}

export default SongsList;

In <BandsList /> I’m using the params props mentioned above. This lets us see if the URL is something like /bands/4/songs. If instead, we’re at the base URL, /bands, we show “No Bands Passed”.

One way of navigating is in this version of <SongsList />. I’ve used the `‘ component from react-router. This will render what appears to be an href but is client-side only. Let’s test it.

Test Client Side Routing

Start up the app with npm run start -s and your browser will open and redirect to /bands and you should see:

Rock and Roll Part 1 - Root Domain

Rock and Roll Part 1 – Root Domain

Click “Click Me” and you should notice 2 things:

  1. The URL has changed. The browsers forward and back buttons should work. Try it.
  2. The BandsList component should now display “4”. React-Router has matched bandid in the URL. It has passed the value down via props to <BandsList />. We’ll use this more in part 2.
Rock and Roll Part 1 - Band Selected

Rock and Roll Part 1 – Band Selected

Don’t worry about any warnings in the console at the moment, we’ll sort them out in the next part.

End of Rock and Roll React.js Tutorial – Part 1

That’s a great place to stop.

So far we have a great foundation to start adding some functionality. We have our app-frame and react-router is setup. Next time we’ll put our state in place and finish off our BandsList component.

If you have any comments, questions or problems, please let me know below or contact me on twitter.

No comments yet, your thoughts are welcome.

Leave a Reply

Your email address will not be published. Required fields are marked *