Overview
I love React Router and how it truly expands the use case for React. Today we will take a quick look at what it does and then dive into 5 features and tricks that I feel are pretty remarkable.
Route to a section:
Refresher
What is it?
React Router is a game-changing package that allows us to turn our React SPA (single page application), into a virtual "multi-page" experience.
Why do we need it?
Typically when we navigate to a new page, the client (browser) sends a request to the server and indicates that a particular route (page) needs to be accessed. (For example, the /about
route would deliver the about.html
file.)
However, React is anything but typical and operates only on the client-side. Therefore we can't request a new page from the server, because we only have access to the index.html
file. So we need a way to at least mimic multiple pages in our SPAs.
What does it do?
React Router intercepts this request on the client-side and instead renders a new component that we specify.
For example, it would render a component called <AboutPage />
which in turn holds <Bio />
and <Skills />
children components that make up the content of that "page".
It looks like we went to a new "About" page, with a new URL slug* and content. In reality, we are still in our trusty index.html
file with its content being rapidly replaced based on routing. 🤯
*The slug follows the main domain and explains what the page is: website.com/this-is-the-slug
Completly new to React Router? Check out the resource links at the bottom to get started on the basics first.
Remarkable Features & Tricks
1️⃣ Fixing Anchor Links
Ok, this is more of a trick than a feature, but you would think creating an anchor link should be simpler than it is.
A logical assumption would be that <Link to='/about#skills'>
would take us to the <About />
component and auto-scroll to the "skills" section.
Sadly this doesn't work out of the box, but there is a simple add-on package that comes to the rescue.
$ npm install --save react-router-hash-link
Import the package into your component.
<Link>
now accepts a hash anchor URL 🎉
...
import { HashLink as Link } from 'react-router-hash-link';
<Link to='/about#skills'>Skills</Link>
2️⃣ Building Relative Paths
There comes a time when we need to nest routes within other components. For example, linking to a blog post within a certain category.
Our target URL should look like: website.com/pets/catsvsdogs
The problem is that React Router will direct you to the main domain website.com/catsvsdogs
and not append the post slug to the /pets/
category as shown above.
This is because by default it treats anything you are linking to as an Absolute path. What we really need is a path relative to the page you are on.
Hard-Code Solution 👎
Hard-coding a relative path is not recommended because if you ever change the parent(s) route further up the URL slug, the path would break.
// Hard-coding the category and post slugs
<Link to='/pets/catsvsdogs'>Cats vs Dogs</Link>
<Route path='/pets' component={blogPost}/>
// The above will break if we make this change to the route
<Route path='/animals' component={blogPost}/>
Dynamic Solution (Hooks) 👍
A much better solution is to leverage the url
property from the match
object that each <Route />
has. This allows us to dynamically place the current route's URL within the <Link />
. and <Route />
.
The useRouteMatch()
hook lets us destructure the url
and the path
properties from the match
object.
The
path
property is similar to theurl
, but is used with<Routes />
.
To make everything truly dynamic, let's also convert the above code into a map that generates a postId
as part of the <Link>
URL parameters. Then we will set up the <Route />
to accept any id, by adding /:postId
at the end of its path.
import { Route, Link, useRouteMatch } from 'react-router-dom';
// Custom Hook
const {path, url} = useRouteMatch();
// Dynamic list of blog post links.
<ul>
{postsArray.map(({id, name}) => (
<li key={id}>
<Link to={`${url}/${id}`}>{name}</Link>
</li>
))}
</ul>
// Route uses the path property to tell which URL it should match and accepts a dynamic id
<Route path={`${path}/:postId`} component={blogPost}/>
Dynamic Solution (Classes) 👍
With class-based components, we can essentially take the same process as above. Instead of using a hook, we access the url
in the match
object via props.
import { Route, Link } from 'react-router-dom';
// Dynamic list of blog post links.
<ul>
{postsArray.map(({id, name}) => (
<li key={id}>
<Link to={`${this.props.match.url}/${id}`}>{name}</Link>
</li>
))}
</ul>
// Route uses props to get the matching url and accepts a dynamic id
<Route path={`${this.props.match.url}/:postId`} component={blogPost}/>
3️⃣ Passing Props via withRouter()
Routes come with a robust set of information that is delivered in the form of props. For example, we can extract params we had set up or a different location to navigate to. (Prop data is stored in location
, match
, and history
objects.)
Often times in our projects we have a component that is not associated with a route but could benefit from the above props. We could pop-drill the data we want, but that could get confusing and messy quickly.
Instead, we can use the higher-order component withRouter()
on the component that just needs quick access to the props. For example, a form that wants to history.push()
to a location such as a confirmation screen.
•••
import { withRouter } from 'react-router';
const Form = () => {
// This function uses the route props to go to a new page after handling the form submission
const handleSubmit = (event) => {
•••
props.history.push(`/confirmation`)
};
<form onSubmit={handleSubmit}>
•••
</form>
}
// Higher-order component that exposes the closest route's props to the Form component
export default withRouter(Form)
4️⃣ Passing Data via URL Query Params
React Router lets us pass data through the URL so that it can be consumed by the linked-to component. Appending this data to the URL is called query parameters.
useLocation()
& custom extraction hook
import {
•••
Link,
useLocation
} from 'react-router-dom';
// React Router suggests this custom hook to pull the value from the url
const useQuery = () => {
return new URLSearchParams(useLocation().search);
}
// Component that has the data we want to send
const ParentComponent = () => {
// Hook from above
let query = useQuery();
return (
<div>
{/* Data is added after the "?" */}
<Link to='/account?name=netflix'>Netflix</Link>
{/* Data is pulled out of the URL and passed as a prop to the child component */}
<ChildComponent name={query.get('name')} />
</div>
);
}
// Component receiving query params props
const ChildComponent = ({name}) => {
return <h1>{name}</h1>
}
useParams()
Hook
import { useParams } from 'react-router-dom';
const Blog = () => {
return (
{/* Link passes in an id as params in the slug*/}
<Link to={`${props.match.url}/${id}`} />Post Name</Link>
{/* Route is set up to dynamically accept any id passed in the slug */}
<Route path=`${props.match.url}/:id`>
<BlogPost />
</Route>
)
}
const BlogPost = () => {
{/* useParams pulls the id param out of the slug so it can be used */}
let { id } = useParams();
return <div>Now showing post {id}</div>;
}
5️⃣ Styling Active Links
A simple upgrade to a site's UX is to show an active style in the navigation for whatever page is showing.
React Router makes this simple with the <NavLink />
component, which replaces the standard <Link />
.
import { NavLink } from 'react-router-dom';
<NavLink to='/' exact>Home</NavLink>
<NavLink to='/about'>About</NavLink>
<NavLink to='/contact'>Contact</NavLink>
// Note that the "Home" link has the prop "exact".
// This prevents it from activating unless it's clicked.
This new component adds an .active
CSS class to any link that has its page shown. We can then target that generated class with any style we prefer.
.nav__link:hover,
.nav__link:active,
.nav__link.active { <--- React Router Generated
color: green;
}
If we don't want to use the class name .active
we can even specify our own name. This just needs to be passed to the activeClassName
prop.
import { NavLink } from 'react-router-dom';
<NavLink
to='/'
exact
activeClassName='nav__link--active' <-------
>Home</NavLink>
Alternatively, we could use the activeStyle
JS styles prop to update the component directly.
import { NavLink } from 'react-router-dom';
<NavLink
to='/'
exact
activeStyle={{
color: 'green'
}}
>Home</NavLink>
Summary
Alright, my friends, that is it for today. I hope you learned something new about React Router that will help your next project.
If you are looking to dig deeper, check out the various resources below. Happy coding! 🤓
Resource Links
Thumbnail designed with Figma
Top comments (1)
Hi you can use anchor links without an external dependency. Just see here dev.to/mindactuate/scroll-to-ancho...