By design, a Router
will render all the Routes
whose paths match the current URL. This allows us to compose layouts in which multiple components should appear or disappear based on the current URL (for example, an application in which the sidebar and main display respond to changes in the current URL). But sometimes, this design choice can produce unintended results.
Consider the following (relatively common) setup:
<Router> <div> <Route path='/articles/new'> <NewArticle /> </Route> <Route path='/articles/:title'> <Article /> </Route> <Route path='/articles'> <Articles /> </Route> </div> </Router>
What should happen when the user navigates to 'articles/new'
? The NewArticle
component should appear, right?
What actually happens is that ALL routes match:
/articles/new
matches exactly/articles/:title
will matchnew
to the URL parameter:title
/articles
will match because both begin with/articles
.
Because all routes match, the application will render the NewArticle
, Article
, and Articles
components simultaneously.
React Router provides several mechanisms for preventing this sort of unintended rendering. The first is the Switch
component:
import { Switch } from 'react-router-dom';
When wrapped around a collection of routes, Switch
will render the first of its child routes whose path
prop matches the current URL.
<Switch> <div> <Route path='/articles/new'> <NewArticle /> </Route> <Route path='/articles/:title'> <Article /> </Route> <Route path='/articles'> <Articles /> </Route> </div> </Switch>
Because the Switch
checks routes sequentially, the order in which Routes are rendered matters. Consider a similar example but with the order of the routes reversed:
<Switch> <div> <Route path='/articles/:title'> <Article /> </Route> <Route path='/articles/new'> <NewArticle /> </Route> <Route path='/articles'> <Articles /> </Route> </div> </Switch>
Now imagine that a user navigates to '/articles/new'
. The Switch
renders the first route with a matching path, '/articles/new'
matches '/articles/:title'
, since :title
is a dynamic segment. With the routes listed in this order, the NewArticle
component will never render. In general, you can avoid this problem by listing routes from most- to least-specific.
Sometimes you may want to leverage React Router’s composability and render multiple routes simultaneously (this would prevent you from using a Switch
component) while also ensuring your router distinguishes between static paths and paths including URL parameters. Consider the following example:
<Router> <div> <Route path='/'> <Home /> </Route> <Route path='/sign-up'> <SignUp /> </Route> </div> </Router>
Any path will match first route, so the the Home
component will be rendered whether the user is at '/'
or '/sign-up'
. This might be ideal behavior if the component rendered by the '/'
route should display regardless of the current route.
But what if you only want the Home
component to be visible to users on the home page and not to those who have navigated to /sign-up
? By using React Router’s exact
prop on the first route, you can ensure that the route will match only if the current URL is an exact match.
<Route exact path='/'> <Home /> </Route>
Now, when a user visits /
, the Home
component will render. But when a user visits /sign-up
, only the second route will match and only the SignUp
component will render.
React Router provides a couple of additional props—strict
and sensitive
—on the Route
component for fine-tuning when a particular route should match, however, these are used far less frequently than the exact
prop.
Instructions
Task 1
If you navigate to /articles/objects
, you will notice that the Article
and Articles
components both render. Let’s fix this by using a Switch
component.
First, import the Switch
component in App.js.
Hint
Import the Switch
component from react-router-dom
and wrap the existing routes in a Switch
.
Task 2
Next, use a Switch
component to wrap all of the Route
components inside the <main>
section. Organize your Route
components such that only the Articles
component renders when you visit '/articles'
and only the Article
component renders when you visit /articles/objects
.
Hint
Make sure to organize your components from most specific to least specific. In this case, the Route
with the path '/articles/:title'
should be above the Route
with the path '/articles'
.