React Router 5 embraces the power of hooks and has introduces four different hooks to help with your routing.
But before we look at hooks themselves, we have to look at a new pattern that the Route
component exposes and how this changes things.
Before:
// 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 a function to the component
syntax would lead to the component re-mounting on every render.
After:
<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 APIs.
But now that implicit passing of route props is not there, 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
history
prop in React Routerpush
, replace
etcimport { useHistory } from "react-router-dom";
function Home() {
const history = useHistory();
return <button onClick={() => history.push("/profile")}>Profile</button>;
}
useLocation
location
prop in React Routerwindow.location
in the browser itself, but this is accessible everywhere as it represents the Router state and location.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 location property is immutable, the
useEffect
will call the function everytime the route changes, making it perfect to operate on search parameters or current path.
useParams
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
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
and list down both routes and customise them with props, now using the hook:
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
likeexact
orsensitive
as an object withuseRouteMatch
.
Using the hooks and the technique we mentioned in the beginning with Route
would make it much easier to upgrade to new versions of React Router.