Learning React Router

In this article, we will explore React Router and create a webpage that can navigate to different sections. We will create a components folder with subfolders for about, contact, footer, header, and home.

The Footer and Header components will remain constant, while the content of other components will change based on user interaction.

An interesting functionality of react-router-dom is Link and NavLink. The Link component can be used instead of the anchor tag to prevent the entire page from refreshing. The NavLink component provides additional features.

<NavLink
  to="/"
  className={({ isActive }) =>
    `block py-2 pr-4 pl-3 duration-200 ${
      isActive ? "text-orange-700" : "text-gray-700"
    } border-b border-gray-100 hover:bg-gray-50 lg:hover:bg-transparent lg:border-0 hover:text-orange-700 lg:p-0`
  }
>
  Home
</NavLink>

Notice that in NavLink, the className is defined inside a callback because it provides an isActive variable. The isActive variable synchronizes with the URL to determine if a particular page is active, allowing us to apply different styles accordingly.

Setting Up Layout with React Router

Since we want the header and footer to remain constant while the rest of the content changes based on user actions, we will utilize the Outlet component from React Router.

import React from 'react'
import { Outlet } from 'react-router-dom'
import Header from './components/Header/Header'
import Footer from './components/Footer/Footer'

function Layout() {
  return (
    <>
      <Header />
      <Outlet />
      <Footer />
    </>
  )
}

export default Layout

The Outlet component will render the child route components, while the header and footer remain the same.

Defining Routes

We will define the routes in the main.js file:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import Layout from './Layout.jsx'
import Home from './components/Home/Home.jsx'
import About from './components/About/About.jsx'
import Contact from './components/Contact/Contact.jsx'

const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />,
    children: [
      {
        path: '', // this matches the root path
        element: <Home />
      },
      {
        path: 'about',
        element: <About />
      },
      {
        path: 'contact',
        element: <Contact />
      }
    ]
  }
])

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)

When the URL is /about, the About component will be rendered in place of the Outlet.

Alternate Route Definition Syntax

There is an alternate, simpler syntax for defining routes:

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path='/' element={<Layout />}>
      <Route path='' element={<Home />} />
      <Route path='about' element={<About />} />
      <Route path='contact' element={<Contact />} />
    </Route>
  )
)

Dynamic Routes

For scenarios like user-specific data on social media platforms, we can create dynamic routes:

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path='/' element={<Layout />}>
      <Route path='' element={<Home />} />
      <Route path='about' element={<About />} />
      <Route path='contact' element={<Contact />} />
      <Route path='user/:userid' element={<User />} />
    </Route>
  )
)

In the User component, we can access the dynamic userid parameter using useParams:

import React from 'react'
import { useParams } from 'react-router-dom'

function User() {
  const { userid } = useParams() // useParams retrieves the id from the URL
  return <div>User: {userid}</div>
}

export default User

Creating a GitHub Page

We can create another route for a GitHub page that shows the profile picture and followers count:

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path='/' element={<Layout />}>
      <Route path='' element={<Home />} />
      <Route path='about' element={<About />} />
      <Route path='contact' element={<Contact />} />
      <Route path='user/:userid' element={<User />} />
      <Route path='github' element={<Github />} />
    </Route>
  )
)

The Github component fetches data from the GitHub API:

import React, { useEffect, useState } from 'react'

function Github() {
  const [data, setData] = useState([])

  useEffect(() => {
    fetch("https://api.github.com/users/rutripathi96")
      .then(res => res.json())
      .then(data => {
        setData(data)
      })
  }, [])

  return (
    <div className='text-center m-4 bg-gray-600 text-white p-4 text-3'>
      GitHub followers: {data.followers}
      <img src={data.avatar_url} alt="GitHub profile" />
    </div>
  )
}

export default Github

We use useEffect to make the API call and useState to store the data received.

Using Loaders in React Router

To optimize the process of making API calls, we can use the loader feature in React Router:

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path='/' element={<Layout />}>
      <Route path='' element={<Home />} />
      <Route path='about' element={<About />} />
      <Route path='contact' element={<Contact />} />
      <Route path='user/:userid' element={<User />} />
      <Route 
        path='github'
        element={<Github />}
        loader={githubInfoLoader}
      />
    </Route>
  )
)

In the Github component, use the useLoaderData hook to access the data:

import React from 'react'
import { useLoaderData } from 'react-router-dom'

function Github() {
  const data = useLoaderData() 

  return (
    <div className='text-center m-4 bg-gray-600 text-white p-4 text-3'>
      GitHub followers: {data.followers}
      <img src={data.avatar_url} alt="GitHub profile" />
    </div>
  )
}

export default Github

export const githubInfoLoader = async () => {
  const response = await fetch('https://api.github.com/users/hiteshchoudhary')
  return response.json()  // returns a promise
}

The useLoaderData hook is used to retrieve the data from the githubInfoLoader function, which fetches the GitHub user data when the cursor hovers over the NavLink, improving the efficiency and user experience.

Usefulness of Loaders in React Router

The loader feature in React Router optimizes the process of making API calls. Instead of waiting for the user to click a link to fetch the data, the loader initiates the API call as soon as the user hovers over the NavLink. This pre-fetching of data ensures that the required information is ready by the time the user navigates to the page, resulting in a smoother and more efficient user experience.


0
Subscribe to my newsletter

Read articles from Rudraksh Tripathi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rudraksh Tripathi
Rudraksh Tripathi