#100DaysOfMERN - Day 44

#100DaysOfMERN - Day 44

·

5 min read

✏ React Router

The react-router package makes it easy to add a navigation to a SPA. In a conventional navigation with <a> tags, each click on the anchor will load a different HTML page. With react-router (more specifially, react-router-dom, which is for applications that run in the browser), a link doesn't lead to a different page, but to a different view, while at the same time making sure that each view has a different URL.

Installing to get started:

npm i react-router-dom

The App component will render the Navigation, and either a Home view, an About view, or a Contact view.

In the Navigation component, all <a> tags are replaced by the imported Link. Instead of an href attribute, it takes a to prop to specify the path:

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

function Navigation() {
    return (
            <nav>
                <Link to="/">Home</Link>
                <Link to="/about">About</Link>
                <Link to="/contact">Contact</Link>
            </nav>
    );
}

✏ Router and Route

Now wrap the whole App with the Router (for brevity, change the name from "BrowserRouter" to "Router" on import):

import { BrowserRouter as Router, Route } from 'react-router-dom';
import Navigation from './components/Navigation.js';
import HomeView from './views/HomeView.js';
import AboutView from './views/AboutView.js';
import ContactView from './views/ContactView.js';

function App() {
    return (
          <Router>
               <Navigation />

               <Route path="/" exact component={HomeView} />
               <Route path="/about" component={AboutView} />
               <Route path="/contact" component={ContactView} />

          </Router>
    );
}

The Route's path corresponds to the Link's to. The keyword "exact" in the Home Route takes care of a little quirk: The Router tries to match the to prop against a path, and "/" is considered a match for all three Routes, because "/", "/about" and "/contact" all include the slash for the Home Route.

To avoid that, you can either

  • use the "exact" keyword

  • add a Switch and put the Home Route at the bottom of the list


✏ Switch

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

function App() {
    return (
          <Router>
               <Switch>

                   <Navigation />

                   <Route path="/about" component={AboutView} />
                   <Route path="/contact" component={ContactView} />
                   <Route path="/" component={HomeView} />

               </Switch>
          </Router>
    );
}

This works because the Switch makes sure that only one view is rendered (the first match).


✏ Passing props

The Routes in the above example are self-closing, and use the component prop to render a view. While this has certain advantages (see below), it's often required to pass some props into a view. This can be done by wrapping the View with the Route:

function App() {
    return (
          <Router>
               <Navigation />

               <Route path="/" exact ><HomeView props={props} /></Route>
               <Route path="/about" ><AboutView props={props} /></Route>
               <Route path="/contact" ><ContactView props={props} /></Route>

          </Router>
    );
}

✏ History

To make sure that the back and forward buttons of the browser still work as expected, the Router provides a history object. If you write the Route as self-closing tag with the component prop, the Router will automatically pass down some props, including the history. You can access it through props.history or by destructuring props:

function HomeView({ history }) {

    return (
          <div className="home"></div>
    );
}

This object has some useful properties and methods, some of which are:

  • history.location.pathname: the current path (the Route's path prop)

  • history.goBack() and history.goForward(): self-explanatory

  • history.push(pathname): move to a different location (for example - after successfully logging in, you can forward a user to their profile screen by pushing /profile)

(to see how to access this object in components that are wrapped by a Route, see below)


✏ Match

The match object is created when a path and the location (url) are successfully matched. You can access it in the same way that you can access the history object above. It has the following properties:

  • match.path and match.url: the two strings that were matched against each other

  • match.isExact: Boolean to indicate whether it was an exact match

  • match.params: object with key/value pairs, parsed from the URL (see below)


✏ Params

These are particularly useful when you have a lot of dynamic routes, a catalogue of products for example.

To link to a product:

<Link to=`/products/${productId}`>Awesome Product</Link>

The route to the product view (the id comes after a colon):

<Route path="/products/:productID" component={ProductView} />

Then in ProductView.js, you get access to the id via match.params:

match.params // {productID: productId}

✏ useHistory, useLocation, useParams

When you nested the view component inside the Route instead of using the component prop, the above mentioned objects won't be implicitly passed down for you, but you can use a few hooks instead to get access to them. To get specific properties, use destructuring:

import { useParams, useHistory, useLocation } from 'react-router-dom';

function ProductView(){

    const params = useParams(); //  the params object
    const { productID } = useParams() // productId (i.e., '12345')

    const location = useLocation(); // the location object
    const { pathname } = useLocation(); // '/products/12345'

    const history = useHistory();

    return (
        <div className="about"></div>
    )
}

✏ Resources

React Router v5: The Complete Guide


✏ Recap

This post covered:

  • how to use React Router

  • Route and Link components

  • history, match and location objects

  • hooks to access these objects when they're not implicitly passed down


✏ Thanks for reading!

I do my best to thoroughly research the things I learn, but if you find any errors or have additions, please leave a comment below, or @ me on Twitter. If you liked this post, I invite you to subscribe to my newsletter. Until next time 👋


✏ Previous Posts

You can find an overview of all previous posts with tags and tag search here:

#100DaysOfMERN - The App