How to use React Router Link component inside an Ant Design Tab component - javascript

it's been since a week that I try to resolve a problem to use React Router Link component with Ant Design Tabs component.
First I basically followed the nested route section from React Router documentation to create a Switch component with route according to desired path.
As I will have to reuse the Tab component on other components later, I create a basic custom TabMenu component into which I pass an array with tab name to map dynamicaly the required tabs.
I tried to wrap a Link component inside the the TabPane component for each mapped value but it doesn't work.
I saw many solution trying to resolve the problem, especially by passing the Link inside the tab props of TabPane component or using the onChange Tab prop with history hook to push to desired location.
I tried both but these solutions seems a little tricky for me and don't really suit me.
The first only work if I click on the tab text because of the Link only affect the text inside tab and not the entire tab. The second work too but the use of history in this case does not seem 'conventional' to me.
I really hope there is a basic solution only with Link component. Here is my code. Thank you
import { Layout, Menu } from 'antd'
import { Link } from 'react-router-dom'
import { AimOutlined, BookOutlined, DashboardOutlined } from '#ant-design/icons'
const { SubMenu } = Menu
const { Sider } = Layout
const SideMenu = () => {
return (
<Sider width={208} style={{
marginTop: 64, overflow: 'auto',
zIndex: 1,
height: '100vh',
position: 'fixed',
left: 0
}}>
<Menu defaultSelectedKeys={['topMenuItem1']} defaultOpenKeys={['sub1']} className={'side-menu'} mode="inline">
<Menu.Item key={'topMenuItem1'} icon={<DashboardOutlined/>}>
<Link to={'/'}> Tableau de bord </Link>
</Menu.Item>
<Menu.Item key={'topMenuItem2'} icon={<BookOutlined/>}>
<Link to={'/missions-catalog'}>Catalogue de missions</Link>
</Menu.Item>
<SubMenu key="sub1" icon={<AimOutlined/>} title="Vos missions">
<Menu.Item key="1">
<Link to={'/company-referent-missions'}> Missions personnelles </Link>
</Menu.Item>
<Menu.Item key="2">
<Link to={'/company-referent-missions/documents'}>Documents</Link>
</Menu.Item>
<Menu.Item key="3">
<Link to={'/company-referent-missions/favoris'}>Favoris</Link>
</Menu.Item>
</SubMenu>
</Menu>
</Sider>
)
}
export default SideMenu
import { Layout, PageHeader } from 'antd'
import { Route, Switch } from 'react-router'
import DashboardEmployeesMissions from '../pages/Dashboard/DashboardEmployeesMissions'
import MyAccount from '../pages/MyAccount'
import CompanyReferentMissions from '../pages/CompanyReferentMissions'
import DashboardHome from '../pages/Dashboard/DashboardHome'
import BreadcrumbNavigation from './BreadcrumbNavigation'
import MissionsCatalogHome from '../pages/MissionsCatalog/MissionsCatalogHome'
const { Content } = Layout
const MainContent = () => {
return (
<Content style={{ marginTop: 64, marginLeft: 208, minHeight: '100vh' }} className="main-content">
<PageHeader title="Tableau de bord" breadcrumb={<BreadcrumbNavigation/>} subTitle="Accueil"/>
<div className={'main-container'} style={{ backgroundColor: '#F7FBFC', padding: '24px' }}>
<Switch>
<Route exact={true} path={'/'}>
<DashboardHome/>
</Route>
<Route path={'/missions-catalog'}>
<MissionsCatalogHome/>
</Route>
<Route path={'/company-referent-missions'}>
<CompanyReferentMissions/>
</Route>
<Route path={'/employees-missions'}>
<DashboardEmployeesMissions/>
</Route>
<Route path={'/my-account'}>
<MyAccount/>
</Route>
</Switch>
</div>
</Content>
)
}
export default MainContent
import React from 'react'
import MissionListItem from '../components/MissionsListItem'
import { Space } from 'antd'
import Title from 'antd/es/typography/Title'
import { Route, Switch, useRouteMatch } from 'react-router-dom'
import MissionCatalogList from './MissionsCatalog/MissionCatalogList'
import { missionApplicationData, missionCatalogData } from '../helpers/DataSeed'
import TabMenu from '../components/TabMenu'
const referentMissionsTabsName = ['Missions', 'Documents', 'Favoris']
const CompanyReferentMissions = () => {
let { url, path } = useRouteMatch()
return (
<Space direction={'vertical'} style={{ width: '100%' }}>
<Space direction={'vertical'}>
<Title level={4}>Mission personnelles</Title>
<TabMenu tabName={referentMissionsTabsName} tabRouterUrl={url}/>
</Space>
<Switch>
<Route exact={true} path={`${path}`}>
<Space direction={'vertical'} size={'large'} style={{ width: '100%' }}>
<MissionListItem headerListTitle={<Title level={3}>Candidatures</Title>}
dataSource={missionApplicationData()}/>
<MissionListItem headerListTitle={<Title level={3}>Missions acceptées</Title>}/>
<MissionListItem headerListTitle={<Title level={3}>Missions terminées</Title>}/>
</Space>
</Route>
<Route path={`${path}/documents`}>
<Space direction={'vertical'} size={'large'} style={{ width: '100%' }}>
<MissionListItem headerListTitle={<Title level={3}>Lettres de missions</Title>}/>
<MissionListItem headerListTitle={<Title level={3}>Attestations de temps passé</Title>}/>
<MissionListItem headerListTitle={<Title level={3}>Ressources</Title>}/>
</Space>
</Route>
<Route path={`${path}/favoris`}>
<Space direction={'vertical'} size={'large'} style={{ width: '100%' }}>
<MissionCatalogList missionsCatalogData={missionCatalogData}/>
</Space>
</Route>
</Switch>
</Space>
)
}
export default CompanyReferentMissions
import { Tabs } from 'antd'
import React from 'react'
const { TabPane } = Tabs
const TabMenu = ({ tabName }) => {
return (
<Tabs defaultActiveKey={1}>
{tabName.map((name, index) => {
return (
<TabPane key={index + 1} tab={name}/>
)
})}
</Tabs>
)
}
export default TabMenu

If you want to show the active tab depending on the location, you can do something like this
import { Tabs } from 'antd'
import React from 'react'
import { useLocation } from 'react-router-dom';
const { TabPane } = Tabs
const TabMenu = ({ tabName }) => {
const location = useLocation();
return (
<Tabs
defaultActiveKey={1}
activeKey={tabName.find((name) => name === location.pathname)}
>
{tabName.map((name, index) => {
return (
<TabPane key={index + 1} tab={name}/>
)
})}
</Tabs>
)
}
export default TabMenu
If you wanted to do something different, please describe in more detail and provide a link to the https://codesandbox.io with a minimal example

Related

dark/light mode material ui react

Im trying to implement dark/light mode in my project but i have problems to put the IconButton with its functionalities inside the Navbar component? The IconButton component is rendered with an onClick function that toggles the color mode between 'light' and 'dark' when it is clicked.
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { Box } from "#mui/material";
import IconButton from "#mui/material/IconButton";
import {
Navbar,
Feed,
VideoDetail,
ChannelDetail,
SearchFeed,
} from "./components";
import { useTheme, ThemeProvider, createTheme } from "#mui/material/styles";
import Brightness4Icon from "#mui/icons-material/Brightness4";
import Brightness7Icon from "#mui/icons-material/Brightness7";
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
const App = () => {
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
return (
<BrowserRouter>
<Box sx={{ bgcolor: "background.default", color: "text.primary" }}>
<Navbar />
<IconButton
sx={{ ml: 2, position: "sticky" }}
onClick={colorMode.toggleColorMode}
color="inherit"
>
{theme.palette.mode === "dark" ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
<Routes>
<Route exact path="/" element={<Feed />} />
<Route path="/video/:id" element={<VideoDetail />} />
<Route path="/channel/:id" element={<ChannelDetail />} />
<Route path="/search/:searchTerm" element={<SearchFeed />} />
</Routes>
</Box>
</BrowserRouter>
);
};
export default function ToggleColorMode() {
const [mode, setMode] = React.useState("light");
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === "light" ? "dark" : "light"));
},
}),
[]
);
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode]
);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</ColorModeContext.Provider>
);
}
Navbar
import React from "react";
import { IconButton, Stack } from "#mui/material";
import { Link } from "react-router-dom";
import { logo } from "../utils/constants";
import SearchBar from "./SearchBar";
const Navbar = () => {
return (
<Stack
direction="row"
alignItems="center"
p={2}
sx={{
position: "sticky",
bgcolor: "background.default",
color: "text.primary",
top: 0,
justifyContent: "space-between",
}}
>
<Link to="/" style={{ display: "flex", alignItems: "center" }}>
<img src={logo} alt="logo" height={45} />
</Link>
<SearchBar />
</Stack>
);
};
export default Navbar;
The IconButton component is rendered with an onClick function that toggles the color mode between 'light' and 'dark' when it is clicked.

Having some error while doing a react restaurant project

RestaurantUpdate.js
import React, { Component } from 'react';
class RestaurantUpdate extends Component {
render() {
console.warn(this.props.match.params.id);
return (
<div>
<h1>Update</h1>
</div>
);
}
}
export default RestaurantUpdate;
App.js
import React from 'react';
import {
Navbar,
Nav,
Container
} from 'react-bootstrap';
import './App.css';
import {
BrowserRouter as Router,
Routes,
Route,
Link
} from 'react-router-dom'
import Home from "./components/Home"
import RestaurantSearch from "./components/RestaurantSearch"
import RestaurantCreate from "./components/RestaurantCreate"
import RestaurantList from "./components/RestaurantList"
import RestaurantUpdate from "./components/RestaurantUpdate"
import RestaurantDetail from "./components/RestaurantDetail"
function App() {
return (
<div className="App">
<Router>
<Navbar bg="dark" variant="dark">
<Container>
<Navbar.Brand href="#home"><img width="75px" height="55px" src="/brand.png" alt="Brand" /></Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto" >
<Nav.Link href="#home" ><Link to="/" style={{ color: 'yellow', textDecoration: 'inherit' }}>Home</Link></Nav.Link>
<Nav.Link href="#link"><Link to="/list" style={{ color: 'yellow', textDecoration: 'inherit' }}>List</Link></Nav.Link>
<Nav.Link href="#link"><Link to="/create" style={{ color: 'yellow', textDecoration: 'inherit' }}>Create</Link></Nav.Link>
<Nav.Link href="#link" ><Link to="/search" style={{ color: 'yellow', textDecoration: 'inherit' }}>Search</Link></Nav.Link>
<Nav.Link href="#link"><Link to="/details" style={{ color: 'yellow', textDecoration: 'inherit' }}>Detail</Link></Nav.Link>
<Nav.Link href="#link"><Link to="/update" style={{ color: 'yellow', textDecoration: 'inherit' }}>Update</Link></Nav.Link>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
<Routes>
<Route path="/list" element={<RestaurantList />} />
<Route path="/create" element={<RestaurantCreate />} />
<Route path="/search" element={<RestaurantSearch />} />
<Route path="/update/:id" render={props => (
<RestaurantUpdate {...props} />
)} />
<Route path="/details" element={<RestaurantDetail />} />
<Route exact path="/" element={<Home />} />
</Routes>
</Router>
</div >
);
}
export default App;
<Route path="/update/:id" render={props => (<RestaurantUpdate {...props} />)}/>
I am getting error in console.
"Matched leaf route at location "/update/1" does not have an element. This means it will render an with a null value by default resulting in an "empty" page."
i was expecting all the params and id in the console.
The update is not displaying as well.
Can anyone tell me error.I guess there is problem with syntax due to updated version. But no sure. I am using the latest version. So kindly tell me where is the error.
You are probably using react-router-dom v6 and above since I see element props in other routes. Also your error says it is missing element here
Matched leaf route at location "/update/1" does not have an element
Try with this modification
<Route path="/update/:id" element={<RestaurantUpdate {...props} />} />
Issue
The path="/update/:id" route isn't rendering any content on the element prop. In react-router-dom#6 the Route component API changed significantly, there is no longer any component or render and children function props and there are no longer any route props, i.e. no location, match, or history props. The RestaurantUpdate component isn't rendered, and even if it was, the this.props.match is undefined.
Solution
Render the RestaurantUpdate component on the element prop and use the useParams hook to access the id path parameter.
<Routes>
<Route path="/list" element={<RestaurantList />} />
<Route path="/create" element={<RestaurantCreate />} />
<Route path="/search" element={<RestaurantSearch />} />
<Route path="/update/:id" element={<RestaurantUpdate />} />
<Route path="/details" element={<RestaurantDetail />} />
<Route exact path="/" element={<Home />} />
</Routes>
Since RestaurantUpdate is a class component you'll need to either convert it to a React function component or create a custom withRouter HOC to inject the path params as a prop.
Convert to function component
import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
const RestaurantUpdate = () => {
const { id } = useParams();
useEffect(() => {
console.warn(id);
}, [id]);
return (
<div>
<h1>Update</h1>
</div>
);
};
export default RestaurantUpdate;
Create a withRouter HOC
import { useParams, /* other hooks */ } from 'react-router-dom';
const withRouter = Component => props => {
const params = useParams();
// other hooks
return (
<Component
{...props}
{...{ params, /* other hook props */ }}
/>
);
};
export default withRouter;
...
import React, { Component } from 'react';
import withRouter from '../path/to/withRouter';
class RestaurantUpdate extends Component {
render() {
console.warn(this.props.match.params.id);
return (
<div>
<h1>Update</h1>
</div>
);
}
}
export default withRouter(RestaurantUpdate);
... however If you dont wanna go with React.FC you may use (as a quick solution) regexp over the:
window.location.pathname.match(/\update\/(\d+)/)
"/update/123".match(/\update\/(\d+)/); // ['update/123', '123', index: 1, input: '/update/123', groups: undefined]

how to Route parent to child component in react router dom

I have an admin dashboard you can check to hear
I want to that if the admin clicks in user(You can see in the screenshot) then it will render the user page and other pages I trying to implement Nested Routeing But not working please if anyone can help it will be appreciated
if someone knows how to render child components and implement them in App.js Please Tell me
Admin.js
import React from 'react'
import { useState } from "react";
import "../AllStyle.css";
import {FaHouseUser, FaTasks, FaUser, FaBars} from "react-icons/fa"
import { NavLink } from 'react-router-dom'
import { AnimatePresence, motion } from "framer-motion";
import SidebarMenu from './SidebarMenu'
const routes = [
{
path: "/user",
name: "Users",
icon: <FaHouseUser />,
},
{
path: "/project",
name: "Project",
icon: <FaTasks />,
},
{
path: "/login",
name: "Logout",
icon: <FaUser />,
},
];
const Admin = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
const showAnimation = {
hidden: {
width: 0,
opacity: 0,
transition: {
duration: 0.5,
},
},
show: {
opacity: 1,
width: "auto",
transition: {
duration: 0.5,
},
},
};
return (
<>
<div className="main-container">
<motion.div
animate={{
width: isOpen ? "200px" : "45px",
transition: {
duration: 0.5,
type: "spring",
damping: 10,
},
}}
className={`sidebar `}
>
<div className="top_section">
<AnimatePresence>
{isOpen && (
<motion.h1
variants={showAnimation}
initial="hidden"
animate="show"
exit="hidden"
className="logo"
>
Evalue portal
</motion.h1>
)}
</AnimatePresence>
<div className="bars">
<FaBars onClick={toggle} />
</div>
</div>
<section className="routes">
{routes.map((route, index) => {
if (route.subRoutes) {
return (
<SidebarMenu
setIsOpen={setIsOpen}
route={route}
showAnimation={showAnimation}
isOpen={isOpen}
/>
);
}
return (
<NavLink
to={route.path}
key={index}
className="link"
// activeClassName="active"
>
<div className="icon">{route.icon}</div>
<AnimatePresence>
{isOpen && (
<motion.div
variants={showAnimation}
initial="hidden"
animate="show"
exit="hidden"
className="link_text"
>
{route.name}
</motion.div>
)}
</AnimatePresence>
</NavLink>
);
})}
</section>
</motion.div>
<main>{children}</main>
</div>
</>
);
};
export default Admin
App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import Navbar from './components/Navbar';
import Contact from './components/Contact';
import Service from './components/Service'
import Login from './components/Login';
// Redirect to their dashboar
import Admin from './components/dashboard/admin/Admin';
import Employee from './components/dashboard/Employee';
import Publisher from './components/dashboard/Publisher';
//Toast error message show
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import Reset from './components/Reset';
import Newpassword from './components/Newpassword';
//admin Routes
import User from './components/dashboard/admin/pages/User'
import Project from './components/dashboard/admin/pages/Project'
function App() {
return (
<div>
<Router>
<Navbar/>
<Routes>
<Route exact path="/" element={<Home/>} />
<Route exact path="/service" element={<Service/>} />
<Route exact path="/contact" element={<Contact/>} />
<Route exact path="/login" element={<Login/>} />
<Route exact path="/reset" element={<Reset/>} />
<Route exact path="/reset/:token" element={<Newpassword/>} />
{/* Redirect to their dashboard */}
<Route exact path="/admin" element={<Admin/>} />
<Route exact path="/employee" element={<Employee/>} />
<Route exact path="/publisher" element={<Publisher/>} />
</Routes>
</Router>
{/* admin routes*/}
<Router>
{/* <Admin> For the admin children route to render children*/}
<Routes>
<Route path="/user" element={<User />} />
<Route path="/project" element={<Project />} />
</Routes>
{/* </Admin> */}
</Router>
<ToastContainer
position="top-right"
autoClose={4000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
</div>
);
}
export default App;
You can use Outlet component.
Example of implementation:
index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import { AuthProvider } from "./utils/context/auth";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<AuthProvider>
<App />
</AuthProvider>
</BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);
app.js
import React from "react";
import { Route, Routes } from "react-router-dom";
import { useAuth } from "./utils/context/auth";
import "react-toastify/dist/ReactToastify.css";
import "./index.css";
import LoginPage from "./app/pages/Auth/Login";
import NotFoundPage from "./app/pages/NotFound/NotFound";
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
const App = () => {
const { checkingSession, isAuthenticated } = useAuth();
if (checkingSession) {
return <Loading />;
}
return (
<>
<Navbar />
<Routes>
{checkingSession || !isAuthenticated ? (
<Route path={"/"} element={<LoginPage />} />
) : (
<Route path="/admin" element={<Layout />}>
<Route path={"/admin"} element={<Dashboard />} />
<Route path={"/admin/project"} element={<Project />} />
</Route>
)}
<Route path={"/*"} element={<NotFoundPage />} />
<ToastContainer
position="top-right"
autoClose={4000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
</Routes>
</>
);
};
export default App;
layout.js
import React from 'react'
import { useState } from "react";
import "../AllStyle.css";
import {FaHouseUser, FaTasks, FaUser, FaBars} from "react-icons/fa"
import { NavLink } from 'react-router-dom'
import { AnimatePresence, motion } from "framer-motion";
import SidebarMenu from './SidebarMenu'
import { Outlet } from "react-router-dom";
// The outlet component
const routes = [
{
path: "/user",
name: "Users",
icon: <FaHouseUser />,
},
{
path: "/project",
name: "Project",
icon: <FaTasks />,
},
{
path: "/login",
name: "Logout",
icon: <FaUser />,
},
];
const Private = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
const showAnimation = {
hidden: {
width: 0,
opacity: 0,
transition: {
duration: 0.5,
},
},
show: {
opacity: 1,
width: "auto",
transition: {
duration: 0.5,
},
},
};
return (
<>
<div className="main-container">
<motion.div
animate={{
width: isOpen ? "200px" : "45px",
transition: {
duration: 0.5,
type: "spring",
damping: 10,
},
}}
className={`sidebar `}
>
<div className="top_section">
<AnimatePresence>
{isOpen && (
<motion.h1
variants={showAnimation}
initial="hidden"
animate="show"
exit="hidden"
className="logo"
>
Evalue portal
</motion.h1>
)}
</AnimatePresence>
<div className="bars">
<FaBars onClick={toggle} />
</div>
</div>
<section className="routes">
{routes.map((route, index) => {
if (route.subRoutes) {
return (
<SidebarMenu
setIsOpen={setIsOpen}
route={route}
showAnimation={showAnimation}
isOpen={isOpen}
/>
);
}
return (
<NavLink
to={route.path}
key={index}
className="link"
// activeClassName="active"
>
<div className="icon">{route.icon}</div>
<AnimatePresence>
{isOpen && (
<motion.div
variants={showAnimation}
initial="hidden"
animate="show"
exit="hidden"
className="link_text"
>
{route.name}
</motion.div>
)}
</AnimatePresence>
</NavLink>
);
})}
</section>
</motion.div>
<main>
<Outlet /> // The outlet component
</main>
</div>
</>
);
};
export default Private
Basically the Outlet will render the routes you put inside the /admin route. More info in this link

How to use React Browser Router in Material UI Drawer

I'm trying to figure out how to use Browser Router in react, to populate the content section of a Material UI Drawer.
The code works to make the menu options links to components that are displayed in the content section of the drawer, however, when the page refreshes, there is an issue, because there is not top level route to the component that is rendered within the content of the drawer. The actual page (that is a top level route) that has the Drawer, should reload on page refresh, but instead, the address in the url that reloads, is trying to use the content component instead.
My code is:
import React, { useState, useEffect } from 'react';
import { Switch, Route, Link, BrowserRouter, useParams,
useRouteMatch } from "react-router-dom";
import { makeStyles } from '#material-ui/core/styles';
import Drawer from '#material-ui/core/Drawer';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Toolbar from '#material-ui/core/Toolbar';
import List from '#material-ui/core/List';
import Typography from '#material-ui/core/Typography';
import Divider from '#material-ui/core/Divider';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import theme from "../../../services/appearance/theme";
import { MuiThemeProvider } from '#material-ui/core/styles';
import Menu1 from "../../menu/First";
import Menu2 from "../../menu/Second";
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
drawer: {
width: drawerWidth,
flexShrink: 0,
},
drawerPaper: {
width: drawerWidth,
},
drawerContainer: {
overflow: 'auto',
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
maxWidth: `calc(100vw - ${drawerWidth}px)`
},
}));
export default function ClippedDrawer() {
const classes = useStyles();
let { path, url } = useRouteMatch();
let { topicId } = useParams();
return (
<div className={classes.root}>
<MuiThemeProvider theme={theme}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
test
</AppBar>
<BrowserRouter>
<Drawer
className={classes.drawer}
variant="permanent"
classes={{
paper: classes.drawerPaper,
}}
>
<Toolbar />
<div className={classes.drawerContainer}>
<List>
<ListItem button key="1" component={Link} to={`${url}/Menu1`}
>
<ListItemText primary="First Menu"></ListItemText>
</ListItem>
<ListItem button key="2" component={Link} to={`${url}/Menu2`}>
<ListItemText>Second Menu</ListItemText>
</ListItem>
</List>
</div>
</Drawer>
<main className={classes.content}>
<Toolbar>test</Toolbar>
<Switch>
<Route path={`${path}/:Menu1`}>
<Menu1 />
</Route>
<Route path={`${path}/:Menu2`}>
<Menu2 />
</Route>
</Switch>
</main>
</BrowserRouter>
</MuiThemeProvider>
</div>
);
}
Does anyone know how to confine the browser router in this particular page, so it only populates the content section of the Drawer - not the actual page url?
The menu page is located at "index.jsx". It is a top level route (equivalent to home/topics in the nesting documentation).
Within that page, is a Material UI Drawer, that has a menu with a link to a component that renders in the content in that index page.
When I load the page for the first time, I get localhost:3000/index. I click on the Menu1 menu item on that page and the url address changes to: localhost3000/Home/Menu1 (and displays the content window of the Material UI Drawer with the component for Menu1 - all correctly). I can refresh that page and the url address remains correct.
However, when I click Menu2 in the Menubar, the url address changes to Home/Menu2, but the content window remains populated with the Menu1 component. It should display Menu2.
How can I adapt the nesting routes example in the documentation to use with the Material UI Drawer?
You have to use Browser Router in App.js. So, it will not give you a blank page. Like this:
<BrowserRouter>
<MarerialUIDrawer/>
<Switch>
<Route exact path='/' render=
{props => <Home {...props} /> }/>
<Route exact path='/about' render=
{props => <About {...props} /> }/>
<Route exact path='/contact' render=
{props => <Contact {...props} /> }/>
</Switch>
</BrowserRouter>
export default App;
I think I found it.
I didn't need the colon in the Route path.
This:
<Route path={${path}/:Menu1}>
Should be:
<Route path={${path}/Menu1}>

Bold active menu after refreshing the page

I have an application has been written with React + Redux and Antdesign. My application is a dashboard app. So I used the layout in Ant design https://ant.design/components/layout/
When I click on side menus, the active menu gets bold which is fine. But I need when I refresh the page, it checks and detect the route and bold related menu item.
I have a Sidebar component which is stateful. Inside it, in componentDidMount I call a function which will dispatch an action from mapDispatchToProps. The reducer changes the state. But in HTML codes, in defaultSelectedKeys, I can not set the number of active menus.
Sidebar.js component:
import React from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux'
import { Switch, BrowserRouter, Route, Link } from 'react-router-dom';
// antd
import { Layout, Breadcrumb, Menu, Icon } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
// Helpers
import { Alert } from '../helpers/notifications';
// Components
import Home from '../components/Home';
// import Header from '../components/Header';
import NotFound from '../components/NotFound';
import PostsEditor from '../components/Posts/PostsEditor';
// Actions
import { setRouteActiveFlag } from '../actions/ui.action'
class Sidebar extends React.Component {
componentDidMount () {
const routes = {
'/' : 1,
'/posts' : 2,
'/logout' : 3
}
this.props.detectActiveRoute(setRouteActiveFlag({
routes:routes,
path:window.location.pathname
}))
}
render() {
const { selectedRoute } = this.props;
console.log(selectedRoute);
return (
<div>
<Layout>
<Sider
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0,
}}
breakpoint="lg"
collapsedWidth="0"
onBreakpoint={broken => {
console.log(broken);
}}
onCollapse={(collapsed, type) => {
console.log(collapsed, type);
}}
>
<div className="logo" >
Logo <br/><br/><br/>
</div>
<Menu theme="dark" mode="inline" style={{ lineHeight: '64px' }} defaultSelectedKeys={[selectedRoute.toString() || '1']}>
<Menu.Item key="1">
<Link to="/" style={{ color:'#fff' }}>
<Icon type="user" />
<span className="nav-text">Home</span>
</Link>
</Menu.Item>
<Menu.Item key="2">
<Link to="/posts" style={{ color:'#fff' }}>
<Icon type="user" />
<span className="nav-text">Posts</span>
</Link>
</Menu.Item>
<Menu.Item key="3">
<a href="/logout" style={{ color:'#fff' }}>
<Icon type="user" />
<span className="nav-text">Logout</span>
</a>
</Menu.Item>
</Menu>
</Sider>
<Layout style={{ marginLeft: 200 }}>
<Content style={{ margin: '24px 16px 0', overflow: 'initial'}}>
<Breadcrumb style={{ margin: '0 0 20px 0' }}>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>List</Breadcrumb.Item>
<Breadcrumb.Item>App</Breadcrumb.Item>
</Breadcrumb>
<div style={{ padding: 24, background: '#fff', minHeight: 360 }}>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/posts/:id?" component={PostsEditor} />
<Route component={NotFound}/>
</Switch>
<Alert stack={ { limit: 3 } } />
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
</Layout>
</Layout>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
state: state,
props: ownProps,
selectedRoute:state.ui.selectedRoute || 1
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
detectActiveRoute: (obj) => dispatch(obj)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Sidebar)
ui.action.js
export const setRouteActiveFlag = (payload = 'global') => ({
type: actions.SET_ROUTE_ACTIVE_FLAG,
payload
});
ui.reducer.js
import { handleActions } from 'redux-actions';
import Immutable from 'seamless-immutable';
import * as actions from '../consts/action-types';
const initialState = Immutable({
requests: {},
selectedRoute:{}
});
export default handleActions({
[actions.SET_ROUTE_ACTIVE_FLAG]: (state, action) => {
if (action.payload.routes && action.payload.path && action.payload.routes[ action.payload.path ]) {
return state.set('selectedRoute', action.payload.routes[ action.payload.path ])
}else{
return state.set('selectedRoute', 1)
}
}
}, initialState);
Please help me find the best and simple practices.
There is no need to use redux, just use react-router to get current pathname and pass it to defaultSelectedKeys.
<Menu defaultSelectedKeys={[this.props.location.pathname]}>
...
.....
</Menu>
Look at this answer , if you don't know how to get pathname
The following answer assumes you are using hooks. I know that from your question you are not using hooks, but it could be useful for other people. This answer works not only when refreshing but also when pressing the back and forward buttons:
import React, { useState, useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import { Layout, Menu } from 'antd'
const { Sider } = Layout
const items = [
{ key: '1', label: 'Invoices', path: '/admin/invoices' },
{ key: '2', label: 'Service Details', path: '/admin/service-details' },
{ key: '3', label: 'Service Contract Details', path: '/admin/service-contract-details' },
{ key: '4', label: 'Cost Centers', path: '/admin/cost-centers' },
{ key: '5', label: 'Clients', path: '/admin/clients' },
{ key: '6', label: 'Vendors', path: '/admin/vendors' }
]
const Sidebar = () => {
const location = useLocation()
const history = useHistory()
const [selectedKey, setSelectedKey] = useState(items.find(_item => location.pathname.startsWith(_item.path)).key)
const onClickMenu = (item) => {
const clicked = items.find(_item => _item.key === item.key)
history.push(clicked.path)
}
useEffect(() => {
setSelectedKey(items.find(_item => location.pathname.startsWith(_item.path)).key)
}, [location])
return (
<Sider style={{ backgroundColor: 'white' }}>
<h3 style={{ paddingLeft: '1rem', paddingTop: '1rem', fontSize: '1.25rem', fontWeight: 'bold', minHeight: 64, margin: 0 }}>
Costek
</h3>
<Menu selectedKeys={[selectedKey]} mode='inline' onClick={onClickMenu}>
{items.map((item) => (
<Menu.Item key={item.key}>{item.label}</Menu.Item>
))}
</Menu>
</Sider>
)
}
export default Sidebar
Your sidebar will look as follows:
You can add any css in your menu by conditioning and adding a class just in this way.
<MenuItem className={ (this.props.location.pathname==='/yourRoute')? 'active' : '' } >
</MenuItem>
In case if you get any kind of undefined error then you can use the 'withRouter' HOC
in this way.
In your component where you want to get that location prop, you will first import
import {withRouter} from 'react-router-dom';
then you can export it in this way.
export default withRouter(YourComponent);
Final code can look somewhat similar to this
import React, {Fragment, Component} from 'react';
import {withRouter, Link } from 'react-router-dom';
class Menu extends Component {
render(){
const {pathname} = this.props.location;
return (
<Fragment>
<div id="sidebar-menu" className="sidebar-menu">
<ul>
<li className={(pathname==='/dashboard' || pathname==='/')?'active':''}>
<Link to="/dashboard">Dashboard</Link>
</li>
<li className={(pathname==='/properties')?'active':''}>
<Link to="/properties">Properties</Link>
</li>
</ul>
</div>
</Fragment>
);
}
}
export default withRouter(Menu);

Categories