In the previous exercise, we created a nested Link
and Route
in the Categories
component.
// Ex: Create a link for the '/categories/html' page <Link to={`/categories/${category}`}> {category} </Link> ... // Ex: When the user visits `/categories/html`, a Category component is rendered <Route path={'/categories/:categoryName'}> <Category /> </Route>
Route nesting improves the organization of Link
and Route
components in our application. As in the Categories
component, it is common that nested Link
and Route
components stem from the same base URL (in this case, the /categories
URL).
Instead of writing out the full URL path, it would be much more flexible if we could create relative paths based on the /categories
URL. React Router provides a hook, useRouteMatch()
, that makes it incredibly easy to do this.
Below, you can see the basic usage in a component called BandPage
that gets rendered by the route '/bands/:band/'
. Suppose that the user visits the page /bands/queen/
. This page should render a list of relative Link
s based on the various songs by the band Queen. A Route
is also created to render a SongPage
for any chosen song:
import { useRouteMatch, Link, Route } from 'react-router-dom'; import { SongPage } from '../SongPage.js' function BandPage ({ songs }) { let { path, url } = useRouteMatch(); // path = '/band/:band' // url = '/band/queen' // Render a list of relative Links and a Route to render a SongPage return ( <div> <ul> { songs.map(songName => <li> <Link to={`${url}/song/${songName}`}> {category} </Link> </li> ) } </ul> <Route path={`${path}/song/:songName`}> <SongPage /> </Route> </div> ) }
Let’s break this down.
useRouteMatch()
should be called inside a component and returns an object with aurl
and apath
property. This object is sometimes referred to as thematch
object:- The
path
property contains the dynamic path pattern with URL parameters (eg./bands/:band
) and should be used for creating relativepath
props forRoute
components (eg./bands/:band/songs/:songName
) - The
url
property has the values of URL parameters filled in (eg./bands/queen
) and should be used for creating relativeto
props forLink
components (eg./bands/queen/songs/we_are_the_champions
).
Let’s see how we can use these values within the Categories
component to create relative routes to the Category
component:
import { Link, Route, useRouteMatch } from 'react-router-dom' function Categories ({ categories }) { let { path, url } = useRouteMatch(); // path = '/categories' // url = '/categories' // Even though path and url are the same in this case, use path for relative Routes and url for relative Links return ( <div> <ul> { categories.map(category => <li> <Link to={`${url}/${category}`}> {category} </Link> </li> ) } </ul> <Route path={`${path}/:category`}> <Category /> </Route> </div> ) }
Using the relative url
and path
values to generate the Link
and Route
components ensures that they accurately route the user to the correct URL regardless of the route that caused the Categories
component to render.
Instructions
Task 1
Let’s now return to the Profile
component. We want to use relative paths to construct the Link
and Route
components for the EditProfileForm
component.
In src/components/Profile.js, import useRouteMatch
from react-router-dom
.
Hint
You can either add useRouteMatch
to the list of existing imports, or import it on its own.
Task 2
Next, inside the Profile
component, use destructuring assignment to get the url
and path
properties from the object returned by useRouteMatch
.
Hint
Your code should look something like this:
const { valueA, valueB } = useRouteMatch();
Task 3
Refactor the Route
and Link
components by replacing hard-coded instances of /profile
with the path
and url
values from useRouteMatch
as appropriate.
Hint
Though both path
and url
will have the same value ('/profile'
), it’s important to consistently use path
when constructing Route
components, and url
when constructing Link
components.
Your code may look like this (using plain string concatenation):
<Link to={url+"/edit"}>Edit</Link> <Route path={path+"/edit"}> <EditProfileForm /> </Route>
or like this (using template literals):
<Link to={`${url}/edit`}>Edit</Link> <Route path={`${path}/edit`}> <EditProfileForm /> </Route>
Note: The url
and path
values are mixed up in the video!