Routing in React With React Router

fosterv2

Valerie Foster

Posted on November 14, 2020

Routing in React With React Router

People who work with React should know that it is essentially a single page application (SPA). But but many React applications don’t present that way to a user. The user interacts with things on the page, and different components appear and disappear. A user interacts with the application as if it has many pages, so it makes sense for the URL to reflect this. This is where React Router comes in.

First of all, since React is a SPA, all routing is Client-Side routing. This is in comparison to server side routing where each different URL makes a different GET request to the server. In Client-Side routing, the only thing the server does is render the HTML with the 'root' div for React to render it’s virtual DOM. One benefit of this is the speed with which the different “pages” will appear for the user. The Client-Side route will just swap which component is rendered to the page instead of making a new server call, which can take some time. But on the flip side, the first page may take longer to load.

The best way to use Client-Side routing in React is to use React Router. This is a React library that uses specific URLs to tell React what components to render at each URL. To use it you have to install react-router-dom to your React app by running:

npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode

Now, to use react-router-dom, you have to decide what file or files you want to define the routes in your React app. The most common places for this is in index.js or the App component if you need to pass state down the component tree. App is usually where you keep the logic of how to organize the application, so it makes sense that it is the component that decides which components to show at each route. You also want to decide what routes you want to have.

For example, lets say you have a React application with a three main components: Home, About, and FriendList, which shows info about each friend in a list you have. At the top of the App.js file you would need to import all the components along with react-router-dom:

import Home from './Home'
import About from './About'
import FriendList from './FriendList'
import { BrowserRouter as Router, Route } from 'react-router-dom'
Enter fullscreen mode Exit fullscreen mode

Importing BrowserRouter as Router is a common convention, so instead of using the term BrowserRouter in the component, you refer to it as Router. There are other things you can import from react-router-dom, but these are the main ones you’ll want to use to apply Client-Side routing.

Now that you’ve got everything imported, you’ll want to use the Router and Route components in the JSX returned from the App component (either the render method of a class component, or the return of a functional component):

return (
  <Router>
    <div>
      <Route exact path=”/” component={Home} />
      <Route exact path=”/about” component={About} />
      <Route exact path=”/friends” component={FriendList} />
    </div>
  </Router>
)
Enter fullscreen mode Exit fullscreen mode

To unpack this example, first we’ll talk about the Router component. It is the base of our application’s routing, so it is where we declare how React Router will be used. It can also only have one child element, so that is why all the Route components are wrapped in a <div>.

Next we’ll talk about the Route component. In this example, they are taking two props, which basically equate to them saying “when the URL matches this path, render the given component.” The exact part just makes sure that the component is only rendered when the URL matches the path exactly. If we omitted the exact from all of the Routes, the Home component would render at any path with a “/” in it, in other words at every path.

Route can also be given the prop of render instead of component. Render takes a callback function as input so our example Routes would look like this:

<Route exact path=”/” render={() => <Home />} />
Enter fullscreen mode Exit fullscreen mode

Using render is best when you have props from App that you want to send to it’s child components. It also has default props that you can pass through the callback function.

Another helpful thing react-router-dom has is the NavLink and Link components. When you import and use either of these components you can add links throughout your pages to other pages in the application. Both components function almost the same way, except you can add styling to NavLinks that show what page you are currently on. So for our example you could have a component called Navbar that you render on every page that looks like this:

import { NavLink } from 'react-router-dom'
const Navbar = () => {
  return (
    <div>
      <NavLink to=”/” exact>Home</NavLink>
      <NavLink to=”/about” exact>About</NavLink>
      <NavLink to=”/friends” exact>Friends</NavLink>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

There is one last piece of routing functionality we are missing, and that is the ability to show the individual friends from our friend list at an individual page. The URL for this is commonly '/friends/1', the 1 being the id of the friend being shown. Writing individual routes for each friend would be ridiculous, so the way we do this is with nested routes.

This requires some refactoring, so first off, we have to change App’s FriendList route to look like this:

<Route
  path='/friends'
  render={props => {
    <FriendList {...props} friends={this.state.friends}/>
  }}
/>
Enter fullscreen mode Exit fullscreen mode

This Route uses render so that it can have access to a list of movies from state and the list of props with information about the route.

Then the FriendList component will render a list of Links that each go to show page for a friend with the URL '/friends/:id' with the id being the id of the friend that’s being shown. It also defines a new Route that uses the match prop passed down:

const FriendList = ({ match, friends }) => {
  const renderFriends => {
    return friends.map(friend => {
      return <Link key={friend.id} to={`/friends/${friend.id}`}>
        {friend.name}
      </Link>
    })
  };
  return (
    <div>
      {renderFriends()}
      <Route
        path={`${match.url}/:friendId`}
        render={props => <Friend {...props} friends={friends} />}
      />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Now we need a way for the Friend component to know which friend from the list it should display. We do this through the match prop again:

const Friend = ({ match, friends }) => {
  return (
    <div>
      <h3>{ friends[match.params.friendId].name }</h3>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

And with this we have fully functioning routes for visiting a a specific friend from the list.

Routing for an application may seem minor when thinking about the entirety of an application, but it can actually be very important. A user could have a favorite page in an app, and if they want to bookmark it, they need a URL to save. Routes can also be expressive to the user about what a certain page of an app is doing. Ultimately, routing is something all developers should take into careful consideration when designing their applications.

💖 💪 🙅 🚩
fosterv2
Valerie Foster

Posted on November 14, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related