React Router 6.0 – Fundamentals

React Router is a library for React that enables navigation and routing in a React application. It allows you to build single-page applications where the content is dynamically loaded and updated as users interact with the application, all without causing a full page refresh. React app, allows users to move between different sections(views) of your application without loading an entirely new web page.

Why do we need it?

  • In traditional multi-page websites, each link or button typically points to a physical HTML file.
  • Clicking on a link triggers a full page reload because it loads an entirely new HTML document.
  • In a single-page application built with React, there is essentially only one HTML page.
  • All views rendered within this single HTML page dynamically as users interact with the application.
  • Traditional navigation, which involves loading separate HTML files, is not the approach in single-page applications.
  • When a user clicks on a link (defined using React Router’s Link), the URL changes, and React Router renders the appropriate component for that route without reloading the entire page.
  • This mechanism provides a smoother experience because only the necessary content (view) changes on the page without a full page refresh.
  • It gives the illusion of navigating between different pages, even though technically, you’re still on the same HTML page.

React Router enables navigation within a single-page application, making it feel like a multi-page application to users. It achieves this by dynamically rendering different components based on the URL, resulting in a more responsive and seamless user experience.

Key concepts

  • <Routes>: Whenever the location(address bar) changes, <Routes> looks through all its child routes to find the best match and renders the relevant component.
  • <Route>: A route defines a specific view or page in your application. For example, you might have a route for your home page, a route for a user’s profile, and so on.
  • <Link>: Instead of using anchor (<a>) tags for navigation, React Router provides a <Link> component. It allows you to create links between different routes in your app without triggering a full page reload.
  • <BrowserRouter>: The <BrowserRouter> component is the top-level component that wraps your entire application. It keeps track of the current URL and renders the appropriate component based on the route.

Install React Router

npm install react-router-dom@latest

Replace latest with the specific version number if you want to install a particular version. Otherwise the command above installs the latest version.

Setup React Router

After completing the installation, the first thing to do is make React Router available throughout your app. To achieve this, open the index.js file in the src folder and import BrowserRouter from `react-router-dom

import { BrowserRouter } from "react-router-dom";

 and then wrap the root component (the App component) in it.

  <BrowserRouter>
    <App />
  </BrowserRouter>

Another option is to perform the same action directly in the parent component, App.js. Refer to Example 1 below.

Creating route

To establish routes in your application, employ the Routes and Route components. We first should import  Routes and Route.

import {Routes, Route} from 'react-router-dom';

The <Routes> component serves as a container for all your routes.

The <Route> component defines a single route with a specified path and element. It takes in two attributes:

  • The path attribute specifies the URL path for the desired component, and you can choose any name for this pathname. In the example below, you’ll observe that the first pathname is a backslash (/). When the app initially loads, any component with a backslash as its pathname will be rendered first. In this case, the <Home/> component will be the initial component rendered
  • The element attribute specifies the component that the route should render.

For instance, if you wish to create three routes for your home page, about page, and contact page, you can achieve this as follows:

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>

Creating links

first you should import <Link> before using it:

import { Link } from 'react-router-dom';

To create links in your application, utilize the Link component. This component acts as a wrapper for the <a> tag, preventing the default browser behavior of reloading the page when clicking on a link. Additionally, it handles the update of the URL and navigation to the corresponding route.

<Link to="/about">About</Link>

Example 1 – Simple routing

import React from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

//This is the parent compoent
const App = () => (
  <BrowserRouter>{/*The components included in the navigation should be wrapped here. It is usually in the parent component or Index.js that we use <BrowserRouter>.*/}
    <div>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>{/*Instead of using the <a> tag, we should use <Link>, and we need to import it in line 2.*/}
          <li><Link to="/about">About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>

      <hr />

      <Routes>
        <Route path="/" element={<Home />} />{/*A route defines a specific view or page in your application. The element is, in fact, the component to be rendered.*/}
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </div>
  </BrowserRouter>
);

//Here are the small components. It is possible to move these component in their respctive file, Home.js,About.js and Contact.js. in that case you need also to impornt them in the begining of App.js.
const Home = () => (
  <div>
    <h2>Home</h2>
    <p>Welcome to the Home page!</p>
  </div>
);


const About = () => (
  <div>
    <h2>About</h2>
    <p>This is the About page.</p>
  </div>
);

const Contact = () => (
  <div>
    <h2>Contact</h2>
    <p>Feel free to contact us!</p>
  </div>
);


export default App;

Navigating programmatically

To implement navigation using JavaScript and without using <Link> in your application, you should use the useNavigate enabling you to navigate to a specific URL or move back or forward in the history stack. For instance, if you intend to create a button that navigates to the Chart view upon being clicked, you can achieve it as follows:

  let navigate = useNavigate();
  return (
    <button onClick={() => navigate("/chart")}>
      Go to Chart
    </button>
  );
}

Nested routes

Nested routes refer to the concept of having routes within other routes. This allows for building complex user interfaces with layouts and shared components across different pages, while still rendering specific content based on the current route. Nested routes are used for example, to display a particular product, or to edit a specific user. Line 4-7 show nested routes.

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/books" element={<BookViewHeader />}>
                 <Route index element={<BookList />} />
                 <Route path="new" element={<NewBookList />} />
                 <Route path="bestselling" element={<BestSellingBookList />} />
                 <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>

<Outlet>

<Outlet> serves as a placeholder for rendering child routes within a parent route.

In an app that uses nested routing, the Outlet component is placed in a parent route component to specify where child routes should render. This allows the parent route(here /books) to display content common to all child routes(/new, /bestselling) while the Outlet is dynamically filled with the content of the currently active child route.

import { Outlet } from "react-router-dom"

export const BookViewHeader = () => (
  <div>
    <h2>My Book Store</h2>
    ...
      {/* This element will render either <NewBookList > when the URL is
          "/new", <BestSellingBookList > at "/bestselling", or <BookList> if it is "/"
      */}
    <Outlet />
  </div>
);

How does the code above work?

  • When a user navigates to “/books/new”, the <NewBookList/> component will be rendered to display the list of new books.
  • Similarly, when a user navigates to “/books/bestselling”, the <BestSellingBookList/> component will be rendered to show the list of bestselling books.
  • In both of these child routes, the <BookViewHeader/> is always rendered.
  • However, it’s crucial to utilize the <Outlet/> component to render the components in the child routes. The Outlet component will render the content of whichever child Route is matched. Without <Outlet/> the component for the child routes will not be rendered.
  • It is also possible to implement the same example as above without using <Outlet /> and with simple non-nested routes, but you would need to repeat BookViewHeader at the beginning of each child component. While this approach may work, it is neither professional nor efficient.
  • It’s important to note that, in line 4, using ‘index’ as a route means that the <BookList/> is the default child component. So, when users access “/books”, both <BookViewHeader /> and <BookList/> will be rendered.
  • The path="*" configuration will match all other addresses, causing the rendering of the <NotFound/> component. This approach allows for redirecting the users to a predefined view, even if they enter an incorrect address.
  • Don’t forget to import the Outlet component when using it.

Dynamic routes with parameter using useParams()

Suppose you have a list of books, and the user can select one of the books to view its details. To achieve this, you need to create a route that sends the Book ID to a component(/books/5), and the component shows only the book with that ID. You can define a nested route as follows.

<Route path=":bookID" element={<Book />} />

To fetch the sent parameter bookID you should use useParams()

import { useParams } from "react-router-dom"

export function Book() {
  const { bookID } = useParams()
  
  return (
    <h1>
       {/*Show the relevant book*/}
    </h1>
  )
}

Example 2 – Nested and dynamic routes

import React from 'react';
//we need to import these to use React Router
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  Outlet,
  useParams,
} from 'react-router-dom';

//<App/> component
//This is the parent compoent
//In this example, we write all components in the same place and the same file 'App.js.' Another alternative is to save each component in its own file and import it here.
const App = () => {
  return (
    <BrowserRouter>{/*We should wrap all components; otherwise, React Router will not work. You can do it even in the index.js file as <BrowserRouter> <App/> </BrowserRouter>. */}
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/books" element={<BookViewHeader />}>{/*Here the nested routes start */}
          <Route index element={<BookList />} />
          <Route path="news" element={<NewBookList />} />
          <Route path="bestselling" element={<BestSellingBookList />} />
          <Route path=":bookID" element={<Book />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
};

export default App;

//<Home/> component
//Since the Home component is very simple, we have removed the 'return' statement and written it as a simple arrow function. Otherwise, the 'return' statement is necessary in larger components.
export const Home = () => <Link to="/books">Book store</Link>;

//<BookViewHeader/> component
//This the component that we share in the routes. Therefore <OutLet/> is in this component. 
export const BookViewHeader = () => (
  <div>
    <h2>My Book Store</h2>
    <div>
      <nav>
        <ul>
        <li>
            <Link to="/books">All Books</Link>
          </li>
          <li>
            <Link to="/books/bestselling">Best Sellers</Link>
          </li>
          <li>
            <Link to="/books/news">News</Link>
          </li>
          <li>
            <Link to="/">Back...</Link>
          </li>
        </ul>
      </nav>
      <hr />
    </div>
    <Outlet />{/*This is the place that children routes are rendered.*/}
  </div>
);

//<NewBookList/> component
export const NewBookList = () => (
<div>
<h1>New Books</h1>  
{/* You can add specific design here to show new books*/}
<BookList category="news" />
</div>
);

//<BestSellingBookList/> component
export const BestSellingBookList = () => (
  <div>
  <h1>Best Sellers</h1>  
  {/* You can add specific design here to show best sellers books*/}
  <BookList category="bestsellers" />
  </div>
  );

//<BookList/> component  
//shows a simple list of books and it is re-used in other components.
export const BookList = ({ category }) => {
  const books = getBooklist(category);
  return (
    <ul>
      {books.map((p) => (
        <li key={p.bookID}>
          <h3>
            <Link to={`/books/${p.bookID}`}>{p.title}</Link>
          </h3>
          <p>{p.author}</p>
        </li>
      ))}
    </ul>
  );
};

//<Book/> component
//show a single book
export const Book = () => {
  const { bookID } = useParams();
  console.log(bookID);
  const book = getBook(bookID);
  return (
    <div>
      <h1>{book.title}</h1>
      <h3>{book.subTitle}</h3>
      <p>{book.author}</p>
    </div>
  );
};

//this function getch a list of books.
export function getBooklist(category) {
  return bookData.filter((p) => p.category == category || !category);
}

//this funciton fetch a single book by its ID
export function getBook(bookID) {
  return bookData.find((book) => book.bookID == bookID);
}

//This array of object contains the books. Each book is an object with properties
const bookData = [
  {
    bookID: 1,
    title: 'Designing User Experience',
    subTitle: 'A Guide to Hci, UX and Interaction Design',
    author: 'David Benyon',
    category: 'bestsellers',
  },
  {
    bookID: 2,
    title: 'Interaction Design',
    subTitle: 'Beyond Human-Computer Interaction',
    author: 'Helen Sharp, Jennifer Preece, Yvonne Rogers',
    category: 'news',
  },
  {
    bookID: 3,
    title: 'Designing Interfaces',
    subTitle: 'Patterns for Effective Interaction Design',
    author: 'Aynne Valencia, Charles Brewer, Jenifer Tidwell',
    category: 'bestsellers',
  },
  {
    bookID: 4,
    title: 'Dont Make Me Think, Revisited',
    subTitle: 'A Common Sense Approach to Web Usability',
    author: 'Steve Krug',
    category: 'bestsellers',
  },
  {
    bookID: 5,
    title: 'A Project Guide to UX Design',
    subTitle: 'For User Experience Designers in the Field or in the Making',
    author: 'Carolyn Chandler, Russ Unger',
    category: 'news',
  },
  {
    bookID: 6,
    title: 'User Experience Design',
    subTitle: 'A Practical Introduction',
    author: 'Allanwood, Gavin, Peter Beare',
    category: 'news',
  },
  {
    bookID: 7,
    title: 'User Interface Design for Programmers',
    subTitle: 'Pocketbok',
    author: 'Avram Joel Spolsky ',
    category: 'news',
  },
  {
    bookID: 8,
    title: 'UX / UI Design',
    subTitle:
      'Introduction Guide To Intuitive Design And User-Friendly Experience',
    author: 'Steven Branson',
    category: 'news',
  },
  {
    bookID: 9,
    title: 'User Interface Design',
    subTitle:
      'Study The Idea Of The Products And Design Requirements: Ux And Ui For Beginners',
    author: ' Woodrow Ito',
    category: 'news',
  },
];

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to Top