« Previous Tutorial Next Tutorial »

Let’s talk front-end routing! For starters, one question you might have is: “What is front-end routing?” This is a good question! Typically when you navigate from, say, localhost/page1 to localhost/page2 , what happens is that the browser makes a request to the server, receives a response, and renders the new page. With client-side routing, the server’s never contacted. Your browser request is intercepted by React (or another framework) and React renders a new set of components. This is all done without a page reload, which speeds things up, and in the case of React, it’s done without even completely rewriting the DOM, which speeds things up even more.

Let’s get routing up and running. It involves installing one new module, and then making a bunch of changes. Open a terminal window to your musiclist directory and type the following:

yarn add react-router-dom

Let that run, and then switch back to Sublime. We’re going to need to wrap our components in our router, but that’s relatively straightforward, at least at the start. Open /src/components/Template.jsx. We’re going to modify this file so that it lets us use different components based on browser URL. Just below line 1, add the following:

import { BrowserRouter as Router, Route } from 'react-router-dom';

You’ll note we’re doing something interesting with destructuring there … not only are we importing two individual properties, BrowserRouter and Route, but we’re renaming BrowserRouter to just “Router” for ease of use. Both of these properties perform essentially as React components, as you’ll see when we adjust the code below.

Below what is now line 5,

import HomePage from './home/HomePage';

add the following code:

import ProfilePage from './account/ProfilePage';

This will allow us to access both of the page components we’ve created from our Template. Next we need to wrap the whole Template component in React-Router’s BrowserRouter wrapper, which remember, we’ve renamed to just “Router” … so take the entire render block, all of this:

      <div className="wrapper">
        <Header username="anonymous" />
        <HomePage />
      </div>

Indent it in one more level, and then wrap it with <Router></Router> so that it looks like this:

    <Router>
      <div className="wrapper">
        <Header username="anonymous" />
        <HomePage />
      </div>
    </Router>

Now we need to add some routes, so let’s do that! Take that central HomePage component, which looks like this:

        <HomePage />

And nuke that jerk, replacing it with the following:

        <Route exact path="/" component={HomePage} />
        <Route path="/account/profile/:id" component={ProfilePage} />

So, let’s cover what we’re doing. It sort of looks like we’re adding both pages to the template, and in a way we are, but because the template is now wrapped with <Router>, React-Router understands that it is to only render these components when the path specified in the “path” prop is being accessed. So when we hit localhost:3000/ it should render HomePage. When we hit localhost:3000/account/profile/ANYID (for example, localhost:3000/account/profile/captaincode), it should render ProfilePage. For any other URL, it should render neither component.

Notice that “exact” boolean prop in the HomePage component? We can set that to make sure the Router understands that “/” is different than “/a” … by not setting it in the second Route, we allow for fuzzy matches. For example, localhost:3000/account/profile/captaincode/random/etc will still bring up the profile page component. This can be highly useful, and we’ll explore it more later.

Save this file. Your React app will hot-reload. If you’re curious, go to localhost:3000/account/profile/captaincode and watch it give you a 404 error. We’ll fix that, but for now, return to localhost:3000/. You’ll see that it’s still showing the HomePage component, as it should. Let’s add some header links so we can quickly flip back and forth between our two pages. Open /src/components/shared/Header.jsx and below line 1 add the following:

import { Link } from 'react-router-dom';

We’re going to be using <Link to=""> instead of <a href=""> for in-app navigation. This takes a little getting used to, and you will type <Link href=""> about a million times. Or, uh, at least, that’s what happened to me. Anyway, just below the <h2> block, add the following code:

        <nav>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/account/profile/cwbuecheler">Profile</Link></li>
          </ul>
        </nav>

That’s it for this one, so save it and head on over to /src/components/account/ProfilePage.jsx so we can make a quick tweak. Change line 3 to this:

export default function ProfilePage(props) {

and directly below it add the following:

const { match } = props;

match is a prop sent automatically by React Router and what it does is, to quote their own documentation: “A match object contains information about how a <Route path> matched the URL.” We can use this to get the :id parameter we defined in our Route, for one thing. So change line 9 to look like this:

This is the profile page. The profile id is: {match.params.id}

Save that file, and we’re ready to test a bit. Your hot-reloader should have those links showing up on the page now, so go ahead and click between them and watch your components change. Cool! But we still have that problem to solve where if we try to manually navigate to anything other than a top-level URL, we get a 404. This is because Express is still taking command of routing whenever a hard request for a new page is made by the browser. What we need to do is create a wild-card that will catch any URL that’s not a part of our API, and route it to /views/index.js so that our React app always gets initialized, at which point, React Router will take over.

So open /app.js and find lines 42 through 44. Right now, we have three routes defined. We want to move the index route down below the API routes, because that’ll allow Express to catch any API routes and NOT send them to the React app, so do that, like this:

app.use('/api', api);
app.use('/api/users', users);
app.use('/', index);

Now all we have to do is add an asterisk to that final line, like this:

app.use('/*', index);

Save the file, and we’re good. Note that this will, of course, make Express’s 404 handling basically useless. That’s OK. We’re going to handle 404s on the React side of things.

All right, we should now be able to navigate to the user profile page without having to click a link to get there. Let’s try localhost:3000/account/profile/captaincode again … and there we go! We’ve got basic React routing up and running. We’ll be tweaking and improving it in future tutorials, but we’re all set for now. Next up, we’ll fix all of that repetitive code we were complaining about in the last tutorial. Catch you then!

« Previous Tutorial Next Tutorial »