Nested Routing not working in react-router version 6 [duplicate] - javascript

I was refactoring my React app after updating React Router to v6 and I got rid of the error I was getting in my routes, except now the desired layout is broken.
I need to include a permanent toolbar and a sidebar to be visible only in some pages. I tried to follow the docs but now the layout component is placed above all the pages it should be wrapping, not just overlapping them, but actually concealing them behind it.
The Layout component:
function Layout({ children }) {
return (
<div className="layout">
<Header />
<SidePanel />
<div className="main" style={{ marginTop: "100px" }}>
{children}
</div>
</div>
);
}
export default Layout;
The AppRouter component:
function AppRouter() {
return (
<Router>
<Routes>
<Route path="/" exact element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/sign-up" element={<SignUp />} />
<Route element={<Layout />}>
<Route path="/diary" element={<Diary />} />
<Route path="/results" element={<Results />} />
<Route path="/details" element={<Details />} />
<Route path="/about" element={<About />} />
</Route>
</Routes>
</Router>
);
}
export default AppRouter;

Layout should render an Outlet for the children Routes to be rendered into.
import { Outlet } from 'react-router-dom';
function Layout() {
return (
<div className="layout">
<Header />
<SidePanel />
<div className="main" style={{ marginTop: "100px" }}>
<Outlet />
</div>
</div>
);
}
Outlet
An <Outlet> should be used in parent route elements to render their
child route elements. This allows nested UI to show up when child
routes are rendered.

Related

React router outlet not rendering the layout

Trying to render dashboard component inside the layout using <Outlet /> but when accessing the /admin/dashboard route, The layout is not rendering. How to fix this?
App.js:
export default function App() {
return (
<div className="App">
<Router>
<Routes>
<Route exact path="/admin" element={<Layout />} />
<Route exact path="/admin/dashboard" element={<Dashboard />} />
</Routes>
</Router>
</div>
);
}
Layout.js:
export default function Layout() {
return (
<Fragment>
<div className="sb-nav-fixed">
<Navbar />
<div id="layoutSidenav">
<div id="layoutSidenav_nav">
<Sidebar />
</div>
<div id="layoutSidenav_content">
<main>
<Outlet />
<Navigate from="admin" to="/admin/dashboard" />
</main>
<Footer />
</div>
</div>
</div>
</Fragment>
);
}
Dashboard.js:
export default function Dashboard() {
return <h1>Dashboard</h1>;
}
Layout routes need to actually have a nested route to be able to have it render its element into the Outlet, not as sibling routes.
export default function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/admin" element={<Layout />}>
<Route path="dashboard" element={<Dashboard />} />
</Route>
</Routes>
</Router>
</div>
);
}
Additionally, if you don't want to render anything on "/admin" then remove the redirect from Layout, remove the path prop from the layout route, and update the path of the dashbard.
Example:
export default function Layout() {
return (
<Fragment>
<div className="sb-nav-fixed">
<Navbar />
<div id="layoutSidenav">
<div id="layoutSidenav_nav">
<Sidebar />
</div>
<div id="layoutSidenav_content">
<main>
<Outlet /> // <-- no redirect now
</main>
<Footer />
</div>
</div>
</div>
</Fragment>
);
}
...
export default function App() {
return (
<div className="App">
<Router>
<Routes>
<Route element={<Layout />}>
<Route path="/admin/dashboard" element={<Dashboard />} />
</Route>
<Route path="*" element={<Navigate to="/admin/dashboard" replace />} />
</Routes>
</Router>
</div>
);
}
If the plan is to render other routes into the layout route then keep the paths as they are and move the redirect to an index route.
export default function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/admin" element={<Layout />}>
<Route index element={<Navigate to="dashboard" replace />} />
<Route path="dashboard" element={<Dashboard />} />
... other admin routes ...
</Route>
</Routes>
</Router>
</div>
);
}

React Router v6 : How to render multiple component inside and outside a div with the same path

I'm trying to upgrade to react-router-dom v6 :
v5
In version 5 it works like a charm:
App.js
import Sidebar from "./components/sidebar/Sidebar";
import Topbar from "./components/topbar/Topbar";
import "./app.css";
import Home from "./pages/home/Home";
import {
BrowserRouter as Router,
Switch,
Route,
} from "react-router-dom";
import UserList from "./pages/userList/UserList";
import User from "./pages/user/User";
import NewUser from "./pages/newUser/NewUser";
import ProductList from "./pages/productList/ProductList";
import Product from "./pages/product/Product";
import NewProduct from "./pages/newProduct/NewProduct";
import Login from "./pages/login/Login";
function App() {
const admin = JSON.parse(JSON.parse(localStorage.getItem("persist:root"))?.user || "{}")?.currentUser?.isAdmin ;
return (
<Router>
<Switch>
<Route path="/login">
<Login />
</Route>
{admin && (
<>
<Topbar />
<div className="container">
<Sidebar />
<Route exact path="/">
<Home />
</Route>
<Route path="/users">
<UserList />
</Route>
<Route path="/user/:userId">
<User />
</Route>
<Route path="/newUser">
<NewUser />
</Route>
<Route path="/products">
<ProductList />
</Route>
<Route path="/product/:productId">
<Product />
</Route>
<Route path="/newproduct">
<NewProduct />
</Route>
</div>
</>
)}
</Switch>
</Router>
);
}
export default App;
v6
When upgraded to v6 I changed my code to be like this:
<Routes>
<Route path="/login" element={<Login />} />
{admin && (
<>
<Route path="/" element={<Topbar />}/>
<Route path="/" element={
<>
<div className="container">
<Route index element={<Sidebar/>}/>
<Route index element={<Home/>}/>
<Route path="/users" element={<UserList />} />
<Route path="/user/:userId" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/productList" element={<ProductList />} />
<Route path="/product/:productId" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
</div>
</>
}
</>
)}
</Routes>
This is my css file for App.js
Notice: the Topbar component should be outside the div, and react router didn't recognize the components inside the as routes even without div, that means each component should have a unique path, I tried also two components with the same path like this:
<Route path="/" element = {<><Home/><Sidebar/><>}, but the css is not taking effect
.container {
display: flex;
margin-top: 50px;
}
It doesn't work. I tried different code and I searched a lot without finding any solution.
Part of the issue is that you are rendering multiple identical paths, i.e. two "/" paths and two nested index paths. This won't work.
In react-router-dom v6 you can create what are called layout components. The layout components can render your headers and footers, sidebars, drawers, and general content layout elements, and importantly an Outlet component for the nested/wrapped Route components to be rendered into.
Example:
import { Outlet } from 'react-router-dom';
const AppLayout = ({ admin }) => admin ? (
<>
<Topbar />
<div className="container">
<Sidebar />
<Outlet />
</div>
</>
) : null;
Render the layout component into a Route wrapping the routes you want to be rendered into the specific layout.
<Routes>
<Route path="/login" element={<Login/>} />
<Route element={<AppLayout admin={admin} />}>
<Route index element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/user/:userId" element={<User />} />
<Route path="/newUser" element={<NewUser />} />
<Route path="/productList" element={<ProductList />} />
<Route path="/product/:productId" element={<Product />} />
<Route path="/newProduct" element={<NewProduct />} />
</Route>
</Routes>
I will share working code from my project, hope this will help you.
Try to create a component layout that should look something like this:
// Layout.js
import React from "react";
import { NavBar } from "./SidebarNav";
export const Layout = ({ children }) => {
return (
<>
<div className="block">
<NavBar />
<div className="w-full ">{children}</div>
</div>
</>
);
};
and then create routes in a similar way:
// routes.js
import { Routes, Route } from "react-router-dom";
import { Layout } from "./layout/Layout";
import Home from "./pages/Home";
import { ItemList } from "./pages/ItemList";
const BaseRouter = () => (
<>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/item-list/" element={<ItemList />} />
</Routes>
</Layout>
</>
);
export default BaseRouter;
Splitting routes into a separate file gives you more freedom and, above all, makes your code more accessible.
// App.js
import { BrowserRouter as Router } from "react-router-dom";
import BaseRouter from "./routes";
function App() {
return (
<Router>
<BaseRouter />
</Router>
);
}
export default App;

Using material ui tabs with react router?

I have a project in react/typescript. I have a react router that looks like this
const Root = () => (
<>
<NavBar/>
<Router>
<Route path="/" component={Home} />
<Route path="/timer" component={TimerPage} />
</Router>
</>
);
And I have a material-ui appbar that looks like this
export default function NavBar() {
return (
<div>
<AppBar position="static">
<Tabs>
<Tab label="Timer" to="/timer" component={TimerPage} />
</Tabs>
</AppBar>
</div>
);
}
There are a few issues - first the 'to' in Tab doesn't compile. Secondly, how do I make these two components work together, given they do very similar things?
If you are trying to navigate to another page, wrap your tab component, let react-router handle with the navigation and navigate using react-router history,
<Tabs value={value} onChange={handleChange} aria-label="simple tabs
example">
<div onClick={() => history.push("/timer")}>
<Tab label="Timer" />
</div>
</Tabs>
<Router>
<Route path="/" component={Home} />
<Route path="/timer" component={TimerPage} />
</Router>
Route should be inside Switch. Also, if you write path="/" this means that whatever page you will visit will still go to "home" page. This is because react-router does something like "least" checking of routes. So, if you had defined path "/images", before "/images/1", both will route you to "/images". Instead you could change the order of these paths, or add exact keyword before the first one.
Take a look at example below.
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/timer" component={TimerPage} />
</Switch>
</Router>
or
<Router>
<Switch>
<Route path="/timer" component={TimerPage} />
<Route path="/" component={Home} />
</Switch>
</Router>
As for your second issue, you should put your AppBar (or div or any container) inside Router and assign Link to component property of Tab:
<Router>
<AppBar position="static">
<Tabs>
<Tab label="Timer" to="/timer" component={Link} />
</Tabs>
</AppBar>
</Router>
Keep in mind that Link component is imported from react-router and not from #mui.

Hide specific components for a route using React Router

In my code, I want Header & Footer components to be rendered in all routes except '/login'so how to do that? How to hide component on a specific route?
const AppRouter = () => {
return (
<BrowserRouter>
<div>
<Header />
<Switch>
<Route path="/login" component={Login} /> {/* I wanna render this route without Header & Footer */}
<Route path="/" component={Home} exact />
<Route path="/product" component={ProductOverview} />
<Route path="/profile" component={Profile} />
<Route component={NotFound} />
</Switch>
<Footer />
</div>
</BrowserRouter>
);
};

useState reset after history push

We have a sidebar that uses history.push to navigate from one page to another in a SPA. When this happens context providers are preserved but the pages useState goes back to its default value. I looked at the code for Link and its not doing anything different then the history push.
We can rewrite to use Link but I read that history push should work and that is all Link is doing anyway. The code below uses route render but have tried with just the JSX like others also.
<Router history={history}>
<div className={classes.body}>
<div className={classes.root}>
<Drawer appConfig={appConfig} />
<div style={{ width: '100%', height: '100vh', overflow: 'hidden' }}>
<Switch>
<Route type="public" path="/dialogs" exact render={Markets} />
<Route type="public" path="/notifications" exact>
<Notifications />
</Route>
<Route type="public" path="/:marketId" exact>
<Market />
</Route>
<Route type="public" path="/:marketId/about" exact>
<About />
</Route>
<Route>
<PageNotFound />
</Route>
</Switch>
</div>
</div>
</div>
</Router>
function Markets(props) {
const { intl } = props;
const { marketDetails, loading } = useAsyncMarketContext();
const [addMode, setAddMode] = useState(false);
function toggleAdd() {
setAddMode(!addMode);
}
function onMarketSave() {
toggleAdd();
}
return (
<Activity
title={intl.formatMessage({ id: 'sidebarNavDialogs' })}
isLoading={loading}
titleButtons={<IconButton onClick={toggleAdd}><AddIcon /></IconButton>}
>
<div>
{!addMode && <MarketsList markets={marketDetails} /> }
{addMode && <MarketAdd onCancel={toggleAdd} onSave={onMarketSave} />}
</div>
</Activity>
);
}
The expectation is that if addMode is true it will retain its value when clicking to /notifications and back.

Categories

Resources