Search code examples
javascriptreactjsreact-router-dommern

The URL changes but component is not rendered when using React Router Dom v6 route in nested page of my react application


I have a main page with a sidebar and a content displaying area which will display the selected option. The problem occurs when I try to click on the Link in sidebar, it doesn't changes anything in the content displaying division but URL changes. If I define the routes in App.js then Link works but it opens the component in new page, I want it to be displayed in the MainPage.js itself.

App.js:

import { useEffect, useState } from 'react';
import {
  BrowserRouter as Router,
  Route,
  Routes,
  Navigate
} from 'react-router-dom'; 
import LoginPage from './pages/LoginPage';
import MainPage from './pages/MainPage';
import SamplePage from './pages/SamplePage'

const App = () => {
  /** HOOKS */

  const [admin, setAdmin] = useState({name: "junaid"})
  // const [admin, setAdmin] = useState(null)

  // hook to see if admin is already logged in
  useEffect(() => {
    const loggedAdmin = window.localStorage.getItem('loggedProSkillzAdmin')
    if (loggedAdmin) {
      setAdmin(JSON.parse(loggedAdmin))
    }
  })

  return (
    <Router forcerefresh={true}>
      {/* <Navigate from="/" to={admin ? "/main" : "/login"} /> */}
      {/* <Navigate exact from="/" to="/main" /> */}
      <Routes>
        {admin === null ? (
          <Route path="*" element={<LoginPage setAdmin={setAdmin} />} />
        ) : (
          // If admin is logged in, navigate to MainPage
          <>
            {/* Redirect from root to /main */}
            <Route path="/" element={<Navigate to="/main" />} />
            <Route path="/main/*" element={<MainPage />} />
          </>
        )}
        <Route path="*" element={<p>No Path Found</p>}/>
      </Routes>
    </Router>   
  )
}

export default App;

MainPage.js:

import { Route, Routes, Link} from 'react-router-dom';
import { MdDashboard } from "react-icons/md";
import { IoIosSettings } from "react-icons/io";
import { FaUsers, FaUserAstronaut } from "react-icons/fa";
import Sidebar from "../components/Sidebar"
import SidebarItem from "../components/SidebarItem"
import SamplePage from './SamplePage';

const MainPage = () => {
  return (
    <div className='flex w-full h-full'>
      <Sidebar>
        <SidebarItem icon={<MdDashboard/>} text="Dashboard" path="dashboard" active={true}/>
        <SidebarItem icon={<FaUsers/>} text="Users" path="users" active={false}/>
        <SidebarItem icon={<FaUserAstronaut/>} text="Providers" path="providers" active={false}/>
        <SidebarItem icon={<IoIosSettings/>} text="Settings" path="settings" active={false}/>
      </Sidebar>
      <div className='w-full h-dvh bg-black'>
        <Routes>
          <Route path="/main/dashboard" Component={<SamplePage title={"Dashboard"} index/>}/>
          <Route path="/main/users" element={<SamplePage title={"Users"}/>}/>
          <Route path="/main/providers" element={<SamplePage title={"Providers"}/>}/>
          <Route path="/main/settings" element={<SamplePage title={"Settings"}/>}/>
        </Routes>
      </div>
    </div>
  )
}

export default MainPage

SidebarItem.js:

import { useContext } from "react"
import { SidebarContext } from "./Sidebar";
import { IoSettings } from "react-icons/io5";
import { Link } from "react-router-dom";

const SidebarItem = ({ icon, text, path, active }) => {
  const { expanded } = useContext(SidebarContext)

  return (
    <>
      <Link
        to={`/main/${path}`}
        className={`
          relative flex items-center py-2 px-3 my-1
          font-medium rounded-md cursor-pointer
          ${active
            ? "bg-indigo-800 text-white"
            : "hover:text-white hover:bg-indigo-800 "
          }
        `}
      >
        <div>{icon}</div>
        <span className={`
          overflow-hidden transition-all
          ${expanded? "w-52 ml-3 visible":"w-0 invisible"}
        `}>
          {text}
        </span>
      </Link>
    </>
  )
}

export default SidebarItem

SamplePage.js:

const SamplePage = ({ title }) => {
  console.log('Sample page works for ', title)

  return (
    <div className="bg-red-700 w-full h-full">
      {title}
    </div>
  )
}

export default SamplePage

Before Clicking the Link

After clicking the Link(notice url changes)

Expecting somehow the component renders in the MainPage.js


Solution

  • Issue

    Descendent routes build relative to the parent route. MainPage is rendered on path "/main/*":

    <Route path="/main/*" element={<MainPage />} />
    

    But is including another "main" path segment in its descendent routes:

    <Routes>
      <Route
        path="/main/dashboard"
        Component={<SamplePage title="Dashboard" index />}
      />
      <Route path="/main/users" element={<SamplePage title="Users" />} />
      <Route path="/main/providers" element={<SamplePage title="Providers" />} />
      <Route path="/main/settings" element={<SamplePage title="Settings" />} />
    </Routes>
    

    This means the URL path is actually "/main/main/dashboard", etc.

    Solution

    Remove the extraneous "main" path segment from the descendent routes. Be sure to correctly use the element prop instead of Component since you are not using a Data router.

    <Routes>
      <Route path="/dashboard" element={<SamplePage title="Dashboard" />} />
      <Route path="/users" element={<SamplePage title="Users" />} />
      <Route path="/providers" element={<SamplePage title="Providers" />} />
      <Route path="/settings" element={<SamplePage title="Settings" />} />
    </Routes>