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;
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>
);
};
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>
);
};