react-router-dom-v6
adidoshi
Posted on February 9, 2022
Hey folks, I know most of us were using react-router-dom-v5 before & the v6 must be a little bit confusing. But trust me they have made it more simpler in the latest version for routing of different pages. Breaking down all the concepts one by one for better understanding. Let's start๐
What is react-router-dom?
- It is a fully-featured client and server-side routing library for react.
- Helps create and navigate between different URLs that make up your web application.
- Provides unique URLs for different components in the app and makes the UI easily shareable with other users.
What we'll be covering from the new version -
- Configuring routes.
- Navigating programmatically
- Dyanamic routes
- Nested routes
- Route parameters
- Lazy loading
Installation -
npm install react-router-dom@6
Configuring routes:
Let's begin with the most common usage for routes.
Taking a scenario, suppose a user is on the home page & want's to visit the about page, vice versa.
- To configure routes, we need to connect url in the browser, with our react app, for that react router provides a component called
BrowserRouter
, with which we need to wrap our entire app. We can simply do that in the index.js file.
// index.js file
import { BrowserRouter as Router } from "react-router-dom";
<Router>
<App />
</Router>
- Create two components home, about and navbar for the UI.
// Home.js file
const Home = () => {
return <div className="element">This is Home page</div>;
};
export default Home;
// About.js file
const About = () => {
return <div className="element"> This is about page</div>;
};
export default About;
- In App.js we wrap home & about within
Routes
component &Route
individually from the react-router-dom. The route has two parameters, path which reflects the path in the url & element which includes our components. Note - Here we don't need to add '/' before path expect for the root/ home url i.e. itself '/'.
// App.js
import { Route, Routes } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Navbar from "./components/Navbar";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
}
- To match the url we wrap the home and about elements in a
Link
component from rrd in the navbar. - For more ease now you can use
NavLink
component provided by rrd, which helps in denoting active page by making that respective element bold on the Navbar.
// Navbar.js
import React from "react";
// import { Link } from "react-router-dom";
import { NavLink } from "react-router-dom";
const Navbar = () => {
const navLinkStyles = ({ isActive }) => {
return {
fontWeight: isActive ? "bold" : "normal",
textDecoration: isActive ? "none" : "underline"
};
};
return (
<nav className="primary-nav">
{/* <Link to="/">Home</Link> */}
{/* <Link to="/about">About</Link> */}
<NavLink style={navLinkStyles} to="/">
Home
</NavLink>
<NavLink style={navLinkStyles} to="/about">
About
</NavLink>
</nav>
);
};
export default Navbar;
Navigating programmatically:
Remember history.push() from v5. Let's suppose you are placing an order on an e-commerce store and when the order is placed you want to navigate the user to another page which says order placed successfully. In that case you want to achieve that with an action button, which is possible by useNavigate provided by rrd.
For example - in home page you have a button 'Place order' you can include an onClick prop. Similarly if you want the user to navigate back to home page from order's page then you can simply use navigate(-1).
import { useNavigate } from 'react-router-dom'
const Home = () => {
const navigate = useNavigate()
return (
<div>
Home page
<button onClick={() => navigate('order-summary')}>Place order</button>
</div>
)
}
export default Home
What if user tries to visit a route, which is not defined, you can easily handle 404 page not found with rrd.
Create a page not found component -
// NoMatch file
const NoMatch = () => {
return <div>Page not found</div>;
};
export default NoMatch;
Simply add a route to App.js ('*' has a special meaning in rrd which means it matches routes which are not defined in your app.
<Route path="*" element={<NoMatch />} />
Nested routes:
React router dom also helps to switch between a portion of the view, inside a page. Take a scenario, we have a navigation link 'Products', when user clicks, it renders the '/products' page & within that we have a search bar then two more links 'featured', 'new'. When we click on featured it renders '/products/featured' & changes only the portion of the page beside links, same when clicked on 'new'. To achieve this we use nested routes
.
- Add products link to the Navbar - (I've attached a codesandbox example for every topic for better understanding)
- Create a products page and include the two links 'featured' and 'new', and make sure you don't add '/' before the path for nested routes. Create two new components, FeaturedProduct & NewProduct. Now here rrd provied an
<Outlet />
component which helps to work this, invoke it below the two links.
// Products file
import { Link, Outlet } from "react-router-dom";
const Products = () => {
return (
<div>
<input placeholder="Search Products" type="search" />
<nav>
<Link to="featured">Featured</Link>
<Link to="new">new</Link>
</nav>
<Outlet />
</div>
);
};
export default Products;
- Now to make this work, we need to enclose the two nested components, in route component by changing it to closing tag from a self closing tag.
Now what is special about nested routes is that rrd automatically forms the full path to the children routes, so featured path is actually
/products/featured
& new is/products/new
. Wohoo! Now we are able to change only the portion of UI in the same page.
// App.js file
<Route path="products" element={<Products />}>
<Route path="featured" element={<FeaturedProduct />} />
<Route path="new" element={<NewProduct />} />
</Route>
Index Route -
In the previous section we saw how nested routes work, however you must have noticed that the child routes render only when the url is '/products/featured' or '/products/new'. Sometimes you may want the child route to render at the parent route level, i.e. if we click on products link, we still want to render the featured products when url reaches '/products', that can be achieved by the index route. It's also a nested routed, so simply specify in the route component & what's special here is we don't need to add path prop instead we just need to add index, this will share the path of the parent route that is '/products'.
Now your App.js file will look like
<Route path="products" element={<Products />}>
<Route index element={<FeaturedProduct />} />
<Route path="featured" element={<FeaturedProduct />} />
<Route path="new" element={<NewProduct />} />
</Route>
Dynamic Routes
Let's suppose we are building an Admin dashboard and we require users list and user details page for particular user (followed by the id of that user). For example if navigate to 'users/1' then it should show details of first user & same for all. We achieve this by dynamic routes in rrd.
- Let's create a Users page and include user list.
// Users.js file
import React from "react";
import { Link, Outlet} from "react-router-dom";
function Users() {
const users = [
{id: 1, name: 'User 1 details'},
{id: 2, name: 'User 2 details'},
{id: 3, name: 'User 3 details'},
]
return (
<div>
{users.map((item) => (
<Link to={`/users/${item.id}`} key={item.id}><h5>{item.name}</h5></Link>
))}
<Outlet />
</div>
);
}
export default Users;
- To match this url, rrd provides us url params which helps in navigating to the individual user details page. Creating the user details page - It's important to note here that userId on the params object corresponds to the dynamic segments, specified in the route config ':userId'
import { useParams } from "react-router-dom";
const UserDetails = () => {
const { userId } = useParams();
// const userId = params.userId; (destructuring userId)
return <div>User details page {userId}</div>;
};
export default UserDetails;
- In App.js we create a Route in which we include path in this manner ->
<Route path="users" element={<Users />}>
<Route path=":userId" element={<UserDetails />} />
</Route>
Search Params
So url params is not the only way to add parameters to the route, we can also add an optional query string. For example if we want to filter active users from the user's list based on some condition then we can add in the url -> /users?filter=active
. This is called as search params in rrd. useSearchParams behaves similar to the useState hook, instead of storing it in memory though it's stored in url.
So in the users file you can just add - (when user clicks on active user useSearchParams is initiated & on reset filters removed.
import React from "react";
import { useSearchParams } from "react-router-dom";
function Users() {
const [searchParams, setSearchParams] = useSearchParams();
const showActiveUsers = searchParams.get("filter") === "active";
return (
<div>
<div>
<button onClick={() => setSearchParams({ filter: "active" })}>
Active users
</button>
<button onClick={() => setSearchParams({})}>Reset filters</button>
</div>
{showActiveUsers ? (
<h2>Show active users</h2>
) : (
<h2>Showing all users</h2>
)}
</div>
);
}
export default Users;
Lazy loading -
Lazy loading is a technique wherein components not required on the home page can be split into separate code bundles and downloaded only when user navigates to that page. Maybe you can think like incrementally downloading the application. It helps reduce initial load time thereby improving performance. Let's understand with an simple example, we will lazy load the about page by adding lot paragraphs meaning trying to make it bulky on purpose.
// About.js file
const About = () => {
return (
<div>
100s.... of lines
</div>
);
};
export default About;
If we observe the bundle size when the entire app is loaded on initial load. Well to check that, open your dev tools, right click on refresh button of your browser and click on 'Empty Cache and Hard Reload'. Taking my example the main.chunk.js is of 10.8kb
& it takes 19ms
to load.
In App.js we need to add dynamic import syntax and make use of React suspense. Make sure in your about page you have a default export, as we need that in App.js file. A promise is returned by this dynamic import which is then converted into a module, that contains a default exported react component, in our case About.
import React from "react";
const LazyAbout = React.lazy(() => import("./components/About"));
<Route
path="about"
element={
<React.Suspense fallback="Loading...">
<LazyAbout />
</React.Suspense>
}
/>
Now when we lazy load the about page, we see the difference. The main.chunk.js file size is reduced to 6.8kb
& time also has reduced to 15ms
. Hence we imporve our initial load time. Maybe you will not able to see the fallback loading text, as the file size in not that large, if you do want to ensure it's working throttle your network speed to 'slow 3g' & you will able to see the fallback element. While you haven't reduced overall code of your app you have reduced the amount of code needed during the initial load & if you think about it as the application increase in size, mote third party packages are installed , the bundle size loads up causing the initial time to be very long.
That is it folks, hope you have gained value from the post, where I've tried to introduce you to almost all topics from the react-router-dom-v6. Further if you want to deep dive more into rrd usage you can refer React Router Dom. There are more hooks like useLocation which would be useful.
Thankyou for visiting.
Posted on February 9, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024
November 27, 2024