Search code examples
javascriptreactjsreact-router-dom

No routes match location in react-router-dom v6


I am migrating to react-router-dom v6. I have nested route in a component and in v5, it works fine. But now it not working as it always saying no routes matched location. I want to put route in my Layout component and then when component is hit, it will call route inside. It is not working. I used to use Switch and BrowserRouter in v5 and everything is good. I can not figure out what is wrong.

Here are my code:

App.js

import React from "react";
import RootRouters from "./components/routers/RootRouters";
import { BrowserRouter as Router } from "react-router-dom";
import "./App.css";

function App() {
    return (
        <Router>
            <div className="App">
                <RootRouters />
            </div>
        </Router>
    );
}

export default App;

RootRouters:

import React from "react";
import { Route, Routes } from "react-router-dom";
import Login from "../auth-page/login";
import Home from "../home/home";
import SignUp from "../auth-page/sign-up";
import AdminLogin from "../../pages/admin/Login";
import Layout from "../../components/admin/layout/Layout";

const RootRouters = () => {
    return (
        <Routes>
            <Route path="/home" element={<Home />} />
            <Route path="/login" element={<Login />} />
            {/* <Route path="/" element={<Home />} /> */}
            <Route path="/register" element={<SignUp />} />
            <Route path="/admin/login" element={<AdminLogin />} />
            <Route element={<Layout />} />
        </Routes>
    );
};

export default RootRouters;

Layout:

import React, { useEffect } from "react";
import "./layout.css";
import Sidebar from "../sidebar/Sidebar";
import TopNav from "../topnav/TopNav";
import AdminRoutes from "../../routers/AdminRouters";
import { BrowserRouter, Route } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import ThemeAction from "../../../redux/actions/ThemeAction";

const Layout = () => {
    const themeReducer = useSelector((state) => state.ThemeReducer);

    const dispatch = useDispatch();

    useEffect(() => {
        const themeClass = localStorage.getItem("themeMode", "theme-mode-light");

        const colorClass = localStorage.getItem("colorMode", "theme-mode-light");

        dispatch(ThemeAction.setMode(themeClass));

        dispatch(ThemeAction.setColor(colorClass));
    }, [dispatch]);

    return (
        <Route
            render={(props) => (
                <div className={`layout ${themeReducer.mode} ${themeReducer.color}`}>
                    <Sidebar {...props} />
                    <div className="layout__content">
                        <TopNav />
                        <div className="layout__content-main">
                            <AdminRoutes />
                        </div>
                    </div>
                </div>
            )}
        />
    );
};

export default Layout;

AdminRoutes:

import React from "react";
import { Route, Routes } from "react-router-dom";
import Dashboard from "../../pages/admin/Dashboard";
import Customers from "../../pages/admin/Customers";
import Products from "../../pages/admin/Products";
import NewDocument from "../../pages/admin/document/NewDocument";
import DetailDocument from "../../pages/admin/document/DetailDocument";
import EditDocument from "../../pages/admin/document/EditDocument";

const AdminRoutes = () => {
    return (
        <Route>
            <Route path="/admin/documents/new" component={NewDocument} />
            <Route path="/admin/documents/detail" component={DetailDocument} />
            <Route path="/admin/documents/update" component={EditDocument} />
            <Route path="/admin/customers" component={Customers} />
            <Route path="/admin/products" component={Products} />
            <Route path="/home" component={Dashboard} />
        </Route>
    );
};

export default AdminRoutes;

Solution

  • Update RootRouter to render the Layout component on a path="*" so it can be involved with route matching, e.g. that it has a path.

    const RootRouters = () => {
      return (
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/login" element={<Login />} />
          {/* <Route path="/" element={<Home />} /> */}
          <Route path="/register" element={<SignUp />} />
          <Route path="/admin/login" element={<AdminLogin />} />
          <Route path="*" element={<Layout />} />
        </Routes>
      );
    };
    

    Update Layout to render the UI directly, Route components no longer have render function props.

    const Layout = () => {
      ...
    
      return (
        <div className={`layout ${themeReducer.mode} ${themeReducer.color}`}>
          <Sidebar />
          <div className="layout__content">
            <TopNav />
            <div className="layout__content-main">
              <AdminRoutes />
            </div>
          </div>
        </div>
      );
    };
    

    Update AdminRoutes to use the React-Router v6 APIs/syntax, namely rendering the Route components into a Routes component, and using the element prop and passing JSX.

    const AdminRoutes = () => {
      return (
        <Routes>
          <Route path="/admin/documents/new" element={<NewDocument />} />
          <Route path="/admin/documents/detail" element={<DetailDocument />} />
          <Route path="/admin/documents/update" element={<EditDocument />} />
          <Route path="/admin/customers" element={<Customers />} />
          <Route path="/admin/products" element={<Products />} />
          <Route path="/home" element={<Dashboard />} />
        </Route>
      );
    };
    

    Alternative suggestion

    If you would prefer to use nested routes you could update the following.

    Update the Layout component to render an Outlet component instead of the AdminRoutes.

    import { Outlet } from 'react-router-dom';
    
    const Layout = () => {
      ...
    
      return (
        <div className={`layout ${themeReducer.mode} ${themeReducer.color}`}>
          <Sidebar />
          <div className="layout__content">
            <TopNav />
            <div className="layout__content-main">
              <Outlet />
            </div>
          </div>
        </div>
      );
    };
    

    Render the "admin routes" directly in RootRouters.

    const RootRouters = () => {
      return (
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/login" element={<Login />} />
          {/* <Route path="/" element={<Home />} /> */}
          <Route path="/register" element={<SignUp />} />
          <Route path="/admin">
            <Route path="login" element={<AdminLogin />} />
            <Route element={<Layout />}>
              <Route path="documents/new" element={<NewDocument />} />
              <Route path="documents/detail" element={<DetailDocument />} />
              <Route path="documents/update" element={<EditDocument />} />
              <Route path="customers" element={<Customers />} />
              <Route path="products" element={<Products />} />
              <Route index element={<Dashboard />} />
            </Route>
          </Route>
        </Routes>
      );
    };