The Hooks of React Router
React Router 5 embraces the power of hooks and has introduced four different hooks to help with routing. You will find this article useful if you are looking for a quick primer on the new patterns of React Router. But before we look at hooks, we will start off with a new route rendering pattern.
Before React Router 5
// When you wanted to render the route and get router props for component
<Route path="/" component={Home} />
// Or when you wanted to pass extra props
<Route path="/" render={({ match }) => <Profile match={match} mine={true} />}>
When using the component
syntax, route props (match
, location
and history
) are implicitly being passed on to the component. But it has to be changed to render
once you have extra props to pass to the component. Note that adding an inline function to the component
syntax would lead to the component re-mounting on every render.
After React Router 5
<Route path="/">
<Home />
</Route>
Note that there is no implicit passing of any props to the Home
component. But without changing anything with the Route
itself, you can add any extra props to the Home
component. You can no longer make the mistake of re-mounting the component on every render and that’s the best kind of API.
But now route props are not being passed implicitly, so how do we access match
, history
or location
? Do we have to wrap all components with withRouter
? That is where the hooks steps in.
Note that hooks were introduced in 16.8 version of React, so you need to be above that version to use them.
useHistory
- Provides access to the
history
prop in React Router - Refers to the history package dependency that the router uses
- A primary use case would be for programmatic routing with functions, like
push
,replace
, etc.
import { useHistory } from 'react-router-dom';
function Home() {
const history = useHistory();
return <button onClick={() => history.push('/profile')}>Profile</button>;
}
useLocation
- Provides access to the
location
prop in React Router - It is similar to
window.location
in the browser itself, but this is accessible everywhere as it represents the Router state and location. - A primary use case for this would be to access the query params or the complete route string.
import { useLocation } from 'react-router-dom';
function Profile() {
const location = useLocation();
useEffect(() => {
const currentPath = location.pathname;
const searchParams = new URLSearchParams(location.search);
}, [location]);
return <p>Profile</p>;
}
Since the location
property is immutable, useEffect
will call the function every time the route changes, making it perfect to operate on search parameters or current path.
useParams
- Provides access to search parameters in the URL
- This was possible earlier only using
match.params
.
import { useParams, Route } from 'react-router-dom';
function Profile() {
const { name } = useParams();
return <p>{name}'s Profile</p>;
}
function Dashboard() {
return (
<>
<nav>
<Link to={`/profile/ann`}>Ann's Profile</Link>
</nav>
<main>
<Route path="/profile/:name">
<Profile />
</Route>
</main>
</>
);
}
useRouteMatch
- Provides access to the
match
object - If it is provided with no arguments, it returns the closest match in the component or its parents.
- A primary use case would be to construct nested paths.
import { useRouteMatch, Route } from 'react-router-dom';
function Auth() {
const match = useRouteMatch();
return (
<>
<Route path={`${match.url}/login`}>
<Login />
</Route>
<Route path={`${match.url}/register`}>
<Register />
</Route>
</>
);
}
You can also use useRouteMatch
to access a match without rendering a Route
. This is done by passing it the location argument.
For example, consider that you need your own profile to be rendered at /profile
and somebody else’s profile if the URL contains the name of the person /profile/dan
or /profile/ann
. Without using the hook, you would either write a Switch
, list both routes and customize them with props. But now, using the hook we can do this:
import {
Route,
BrowserRouter as Router,
Link,
useRouteMatch,
} from 'react-router-dom';
function Profile() {
const match = useRouteMatch('/profile/:name');
return match ? <p>{match.params.name}'s Profile</p> : <p>My own profile</p>;
}
export default function App() {
return (
<Router>
<nav>
<Link to="/profile">My Profile</Link>
<br />
<Link to={`/profile/ann`}>Ann's Profile</Link>
</nav>
<Route path="/profile">
<Profile />
</Route>
</Router>
);
}
You can also use all the props on Route
like exact
or sensitive
as an object with useRouteMatch
.
Wrapping up
The hooks and explicit Route
comes with a hidden advantage in itself. After teaching these techniques at multiple workshops, I have come to the realization that these help avoid a lot of confusion and intricacies that came with the earlier patterns. There are suddenly far fewer unforced errors. They will surely help make your router code more maintainable and you will find it way easier to upgrade to new React Router versions.
The post The Hooks of React Router appeared first on CSS-Tricks.