- Use React Router to implement single-page client-side routes
- Use Switch, Route, NavLink, and history.push() to implement client-side routing
- Use Hooks for programmatic navigation
- useHistory()
- useLocation()
- useParams()
- Segment 1: SPA Architecture + Pros & Cons of Client-Side Routing
- Install and setup react router
- Use Switch and Route to set up initial routes
- Segment 2: Client-Side Routing
- Use Link / NavLink and Router props to implement show page
- Segment 3: Programmatic Navigation
- Explore useHistory(), useLocation(), and useParams() use cases
Decide what frontend routes we want:
<Home>
<ProjectForm>
<ProjectList>
<ProjectDetail>
Why do we even need routes?
- The user can use forward/back to navigate your app
- The user can navigate via urls
- We can make urls describe the action that the user might be taking
- Users can bookmark urls
What are the drawbacks to client-side routing?
- We're loading all of our frontend at once, so it might add to the initial load time
- We have to design all of our routes to be coupled with our component structure (which can be a good thing long-term)
At a low level, React Router takes advantage of the fact that we can programmatically interact with the browser's url with the History API.
You can manipulate the URL in your browser with these:
window.history.back(); // go back
window.history.forward(); // go forward
window.history.pushState({}, "", "pets"); // go to the /pets page
We can also interact with the URL using the Location API:
console.log(window.location.pathname);
// => "/pets"
Combine that with if/else
logic and tracking history and you get react-router
.
npm install react-router-dom
To set up our application to use a router, we wrap our ENTIRE application in a
Router
component:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router } from "react-router-dom";
import App from "./App";
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
);
React Router is essentially a Context Provider that gives your other components access to the history object so you have access to additional information to perform routing logic.
Note that there are several kinds of routers:
- BrowserRouter: gives nicest looking URLs, but can be trickier to deploy (check your host's documentation on client side routing to see how to configure it if you need this style of URL).
- HashRouter: all urls start with a # at the beginning. Easier to configure for deploying, but has some limitations.
IMO, it's worth the extra effort to get BrowserRouter working!
Once your Router component is set up, you can create separate routes that will each be responsible for showing separate components. If we want to use the following routes in our application:
- "/" -
HomePage
component - "/about" -
AboutPage
component
In the code below, we're relying on two components from React Router to set up these different routes:
Switch
: Looks through all its child Route components and returns the first one that matches the current URL (like aswitch
statement in JavaScript)Route
: Uses a prop of path to compare with the current URL; if the path matches the url, it will render a component based on its component or render prop- Use
exact
prop if you only want exact matches!
- Use
// App.js
import React from "react";
import { Route, Switch } from "react-router-dom";
import HomePage from "./HomePage";
import AboutPage from "./AboutPage";
import UserPage from "./UserPage";
import NavBar from "./NavBar";
function App() {
return (
<div className="App">
<NavBar /> {/* NavBar will be displayed on all routes */}
<Switch>
<Route path="/about">
<AboutPage />
</Route>
<Route path="/users/:name">
<UserPage />
</Route>
<Route exact path="/">
<HomePage />
</Route>
</Switch>
</div>
);
}
Since we're implementing client side routing, we can't rely on just making
regular <a>
tags to navigate between our pages; if we use an anchor tag, it
will trigger a refresh of our application and we'll lose our application state!
Instead, we need to use the Link
component from React Router to keep all of
our navigation between pages handled within Javascript.
import React from "react";
import { Link } from "react-router-dom";
const NavBar = () => {
return (
<div class="NavBar">
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</div>
);
};
export default NavBar;
react-router
comes with several custom hooks that give us access to the internals
of React Router:
useParams
: access any dynamic parts of the url asparams
- If our url is
/projects/1
, and we're on the route/projects/:id
, thenuseParams
will return{ id: 1 }
- If our url is
useHistory
: access the browser's history to allow programmatic navigation- To navigate the user to
/page
, we'd usehistory.push("/page")
- To navigate the user to
useLocation
: returns the location object, with info like the full URL for the current page and the current path.useRouteMatch
: used to match the current URL to a given string, similar to the way<Route>
would. Shows what route is currently being matched.
To work with these hooks, first import the one you'd like to use. Then, call it
in the body of your function component, just like you would with useState
or
useEffect
.
import { useParams } from "react-router-dom";
function MyPage() {
const params = useParams();
return <h1>Page params: {params.id}</h1>;
}