Written by Yusuff Faruq✏️
In May of 2019, Ryan Florence, co-creator of React Router and Reach Router, announced the impending release of a new version of React Router that takes advantage of React’s Hooks API. He also stated that React Router would be the surviving project, while Reach Router will continue to receive support in the form of bug fixes.
Fast-forward to September 2019, and React Router v5.1 was eventually released. This version is an introduction to the new Hooks-based API and comes with some amazing features. The new Hooks also make routing easier.
In this article, I will talk about the new features in React Router, compare Reach Router (and React Router) to the new Hooks-based API, and briefly discuss how to migrate to this API.
The Hooks
The useHistory
Hook
A new addition to React Router is the useHistory
Hook, which gives you access to the “history” instance from the history package, one of React Router’s major dependencies. The history object allows for programmatic navigation between routes in your React apps.
In React Router v4, to access the history
object, you had to use the history
prop.
Let’s say we wanted to programmatically navigate to a route called home
using a button. With React Router v4, our code would look similar to this:
function HomeButton({history}) {
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
However, with the introduction of the useHistory
Hook, we can easily access the history
object and use it like so:
import { useHistory } from "react-router-dom";
function HomeButton() {
const history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
If the user clicks on the button, the home
entry will be pushed onto the history
stack, and the home page will be rendered.
The useLocation
Hook
The next Hook we will discuss is useLocation
. This Hook returns the location
object, which represents the current URL. The location
object can also be used to access data sent from another route using the location
object’s state property.
In React Router v4 and Reach Router, to gain access to the location
object, you had to access them using props or, in the case of Reach Router, a Location
component.
This is how you would access the location
object with React Router v4:
function RandomRoute({ location }) {
return <h1>Current pathname: {location.pathname}</h1>;
}
And this is how you would access the location
object in Reach Router:
function RandomRoute() {
return (
<Location>
{({ location }) => <h1>Current pathname: {location.pathname}</h1>}
</Location>
);
}
Now, with the new useLocation
Hook, you can access the location
object more conveniently, like so:
function RandomRoute() {
const location = useLocation();
return <h1>Current pathname: {location.pathname}</h1>;
}
The useParams
Hook
React Router v5.1 also gives us the new useParams
Hook. This Hook returns an object of key-value pairs of URL parameters. URL parameters, commonly used among React Router and Reach Router users, allow us to conveniently pass information about a click through a URL.
In Reach Router and previous versions of React Router, the only way we could access URL parameters was through props and, in the case of Reach Router, the Match
component.
So, with React Router v4, we would have to do something like this:
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
function App() {
return (
<Router>
<header>
<nav>
<Link to="/">Home</Link>
<Link to = "/page/2">Page 2</Link>
</nav>
</header>
<Switch>
<Route path = "/page/:pageNumber" component = {Page}>
<Route path="/" render={() => <h1>Home</h1>} />
</Switch>
</Router>
);
}
function Page({match}) {
const {pageNumber} = match.params;
return <h1>Page Number:{pageNumber}</h1>;
}
Or something like this, in the case of Reach Router:
import { Router, Link } from "@reach/router";
function App() {
return (
<>
<header>
<nav>
<Link to="/">Home</Link>
<Link to="/page/2">Page 2</Link>
</nav>
</header>
<Router>
<Home path="/" />
<Page path="/page/:pageNumber" />
</Router>
</>
);
}
const Home = () => <h1>Home</h1>;
function Page(props) {
return <h1>Page Number:{props.pageNumber}</h1>;
}
The above methods work fine for most cases. But if you are trying to pass URL parameters down to child components, you would have to pass them them as props, which can make your code messy.
With the new Hooks API, you can easily call the useParams
Hook in any child component to get the URL parameters. If we were to rewrite our code to use Hooks, it would look something like this:
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams
} from "react-router-dom";
function App() {
return (
<Router>
<header>
<nav>
<Link to="/">Home</Link>
<Link to = "/page/2">Page 2</Link>
</nav>
</header>
<Switch>
<Route path = "/page/:pageNumber">
<Page />
</Route>
<Route path="/" render={() => <h1>Home</h1>} />
</Switch>
</Router>
);
}
function Page() {
const {pageNumber} = useParams();
return <h1>Page Number:{pageNumber}</h1>;
}
The useRouteMatch
Hook
The last new Hook is useRouteMatch
. In Reach Router, if you wanted to access the match
object of a Route, you would have to use the Match
component. And if you were using a previous version of React Router, you would have to use the route’s props or render props. With this new Hook, it’s easier and more convenient to access match
object!
The Hook takes in a path as an argument and returns a corresponding match
object. When no argument is passed, the Hook returns a match
object based on the closest matching <Route>
in the tree.
Former way of accessing a match object in React Router:
//option 1
function ARoute() {
return (
<Route
path="/randomroute/:randomrouteid"
render={({ match }) => {
return (
...
);
}}
/>
);
}
//option 2
function ARoute(props){
const match = props.match;
return (
...
);
}
To get the match
object in Reach Router, we would have to use the provided Match
component:
function AnotherRandomRoute(){
return(
<Match path = "/randomroute/:randomrouteid">
{
({match}) => ...
}
</Match>
);
}
The above code blocks work fine, but we can make our code shorter and cleaner with the useRouteMatch
Hook, like so:
function AnotherRandomRoute(){
const match = useRouteMatch("/randomroute/:randomrouteid");
return(
...
);
}
With useRouteMatch
, you can also implement nested routing using the url
and path
properties of the match
object. Here’s an example of how you might handle nested routing in React Router with this Hook:
function Topics() {
const { path, url } = useRouteMatch();
return (
<div>
<div>
<Link to={`${url}/1`}>Topic 1</Link>
<Link to={`${url}/2`}>Topic 2</Link>
<Switch>
<Route exact path={path} render={() => <h1>Select a topic</h1>} />
<Route path={`${path}/:topic`}>
<Topic />
</Route>
</Switch>
</div>
</div>
);
}
function Topic() {
const { topic } = useParams();
return (
<div>
<h1>Topic: {topic}</h1>
</div>
);
}
useRouteMatch
is also useful any time you would use a Route
component outside a Switch
component.
Updates to the Link
and NavLink
components
React Router v5.1 also added some updates to the Link
and NavLink
components, one of which is the ability to pass in functions to these components’ to
props. The current location is passed as an argument to the function, and this function must return a location representation in the form of an object or a string.
React Router v6
Currently, a major version of React Router is in the works. React Router v6, which is currently in the alpha stage, will have the following features:
- A smaller bundle size. The current size of the new version in a fully migrated app is around 3kB. According to a tweet by Michael Jackson, co-creator of React Router, they were able to achieve this by dropping support for anything older than IE11, dropping support for React <16.8, using Google Closure Compiler, and writing better code
- Automatic
<Route>
ranking with a new<Routes>
API.<Routes>
will replace<Switch>
- Nested route configs (much like React Router v3 and Reach Router)
- New Suspense-ready
navigate
API -
useRoutes
andmatchRoutes
for using object-based routing API - A new Hook called
useNavigate
, which returns a function for programmatic routing/navigation.
Migrating from Reach Router to the new Hooks-based API
If you are planning to migrate from Reach Router, it’s easier to migrate to React Router v6 because they look similar at surface level. You can easily migrate by following these steps:
- Install React Router v6
- Replace
<Location>
and<Match>
with theuseLocation
anduseRouteMatch
Hooks - Use
useParams
to access URL parameters - Put a
<BrowserRouter>
on top:
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
- Replace
<Router>
with<Routes>
and define your routes using<Route>
Conclusion
So far, React Router v6 is looking very promising. The new features and Hooks will definitely encourage cleaner code, and I think it was a great decision on the part of the React Router team to move towards building a Hooks-based API.
Full visibility into production React apps
Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.
LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your React apps — start monitoring for free.
The post The future of Reach Router and React Router appeared first on LogRocket Blog.
Top comments (0)