Valerie Foster
Posted on November 14, 2020
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
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'
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>
)
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 Route
s, 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 Route
s would look like this:
<Route exact path=”/” render={() => <Home />} />
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 NavLink
s 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>
)
}
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}/>
}}
/>
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 Link
s 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>
)
}
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>
)
}
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.
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
September 12, 2024