Migrating React Router from 3 to 4

I was working on understanding how to make React relevant beyond a single page app, and I came across this tutorial by Krithika Muthukumar.

When I got to the end, and I knew what I expected each piece to be doing, nothing happened. None of the components worked at all. I dug in deeper, and saw that some of the required packages were outdated, and after some elimination processes, I found the problem: it used React-Router 3, and the latest version, 4, has changed significantly.

As a challenge to myself, someone who is pretty new to React and with no previous experience with React-Router, I attempted to migrate the tiny, crazy simple app to React-Router 4. And after some brow furrowing and chin scratching, I made it work. This may not be the best way to do things, and there are some things I thought would work but don’t (and I still don’t know why). But it works, and I’m happy about that.

I also made this crazy simple app a tiny bit more complicated by adding a header with links to each of the two pages.

Here’s what I did. (Click here for the Github repo)

Install React Router 4

npm install --save react-router react-router-dom

src/index.jsx

In the index, we’ll have React.DOM render the App component, instead of a Router component. That means we don’t need to import anything from react-router or ./routes, and we remove the Router component.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/app';

require('./stylesheets/base.scss');
require('./stylesheets/home.scss');
require('./stylesheets/contact.scss');

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

src/components/app.jsx

First, we’ll need to import BrowserRouter from react-router-dom, as well as the Routes component and a new Header component.

Our App is going to return BrowserRouter so all the child components will have access to the routes. There are a couple of things of note about BrowserRouter, which replaces Router. It comes with history included, so we don’t need to worry about managing it. Also, it can only take one child, so we put everything in a <div>.

import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import Header from './views/partials/header';
import Routes from '../routes';

const App = () => (
  <BrowserRouter>
    <div>
      <Header />
      <Routes />
    </div>
  </BrowserRouter>
);

export default App;

src/routes.jsx

Back up to Routes. This is a bit different than in React Router 3. We’re going to use Switch, which finds the first matching route and ignores the rest. For the catchall route, we can render() a function to Redirect back to ‘/‘.

import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import Home from './components//views/home';
import Contact from './components//views/contact';

const Routes = () => (
  <Switch>
    <Route exact path='/' component={Home} />
    <Route path='/contact' component={Contact} />
    <Route path='*' render={() => <Redirect to='/' />} />
  </Switch>
);

export default Routes;

src/components/views/partials/header.jsx

I created a partials directory for the Header component. It needs to import Link from react-router-dom to use the routes in Routes.

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

const Header = () => (
  <div id='Header'>
    <span><Link to='/'>Home</Link></span> | <span><Link to='/contact'>Contact</Link></span>
    <hr/>
  </div>
);

export default Header;

src/components/views/home.jsx and src/components/views/contact.jsx

The only significant change here is to remove the componentDidMount() method, which means we don’t need to import anything from react-router.

import React from 'react';

const Home = () => (
  <div id='home'>
    This is the home page.
  </div>
);

export default Home;

And

import React from 'react';

const Contact = () => (
  <div id='contact'>
    This is the contact page.
  </div>
);

export default Contact;

And that’s all there is to it. I’m extremely grateful to Krithika Muthukumar for creating the tutorial originally. I have been having a very hard time finding help wrapping my brain around how a multi-page React/Express app would work, and this has helped enormously. I’m also grateful to React Training for the documentation and for making React Router in the first place. Powerful stuff here.