Set Antd's Menu defaultSelectedKeys value using React and Redux - reactjs

I'm using antd for building a react application. I'm using antd's Layout to design the layout. Also, I'm using react-router for routing and redux for state management.
In antd's Menu I've set the defaultSelectedKeys to a blank array so that when page loads I don't want to show the active menu item. Instead, When the user accesses a certain page{Component) using application URL in the browser, then that page's menu item should be active.
For example, by default defaultSelectedKeys array will be blank. So no menu item will be active initially. When the application loads and react-router routes to the home component then the home menu item should be active.
For this, I'm using useEffect hook to dispatch an action with the menu key when a particular component is mounted.
Example Home Component:
import React, { Fragment, useEffect } from "react";
import { useDispatch } from "react-redux";
import { CURRENT_COMPONENT } from "./../reducers/types";
export default function HomeComponent() {
const dispatch = useDispatch();
useEffect(() => {
dispatch({
type: CURRENT_COMPONENT,
payload: { component: "Landing", sideBarMenuKey: "1" }
});
}, [dispatch]);
return (
<Fragment>
<h1>This is a home componet </h1>
<br />
...
<br />
<br />
...
<br />
<br />
...
<br />
</Fragment>
);
}
Up here in the useEffect I'm dispatching an action to the reducer with sideBarMenuKey: "1" and it is dispatching perfectly when this component mounts and able to receive the state change in Layout component as well using the react-redux useSelector hook.
Example Layout Component:
import React, { Fragment } from "react";
import { useSelector } from "react-redux";
import { Layout, Menu, Breadcrumb } from "antd";
import { Switch, Route } from "react-router-dom";
import HomeComponent from "./Home";
import AboutComponent from "./About";
const { Header, Content, Footer } = Layout;
export default function LayoutComponent() {
const sideBarMenuKey = useSelector(
state => state.currentComponetReducer.sideBarMenuItemKey
);
console.log(sideBarMenuKey);
return (
<Fragment>
<Layout className="layout">
<Header>
<div className="logo" />
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={[sideBarMenuKey]}
style={{ lineHeight: "64px" }}
>
<Menu.Item key="1">Home</Menu.Item>
<Menu.Item key="2">About</Menu.Item>
</Menu>
</Header>
<Content style={{ padding: "0 50px" }}>
<Breadcrumb style={{ margin: "16px 0" }}>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>List</Breadcrumb.Item>
<Breadcrumb.Item>App</Breadcrumb.Item>
</Breadcrumb>
<div style={{ background: "#fff", padding: 24, minHeight: 280 }}>
<Switch>
<Route exact path="/" component={HomeComponent} />
<Route exact path="/about" component={AboutComponent} />
</Switch>
</div>
</Content>
<Footer style={{ textAlign: "center" }}>
Ant Design ©2018 Created by Ant UED
</Footer>
</Layout>
</Fragment>
);
}
Even though I'm receiving the state change in the Layout Component and when I use sideBarMenuKey in antd's Menu prop as defaultSelectedKeys={[sideBarMenuKey]} the menu item doesn't get active state.
Sample Example:
I've created a sample codesandbox example. The link is below
https://codesandbox.io/s/styled-antd-react-starter-ytxko
I've created a Layout, Home and About components. Initially, when you log in it will show Home component. If you want to render the About component, then change the browser URL in the codesandbox to https://url/about. Both in Home and About component I'm dispatching an action with sideBarMenuKey and the state is also getting updated. But the menu item is not getting active.

Changing defaultSelectedKeys to selectedKeys should make it work:
<Menu
theme="dark"
mode="horizontal"
selectedKeys={[sideBarMenuKey]}
style={{ lineHeight: "64px" }}
>
<Menu.Item key="1">Home</Menu.Item>
<Menu.Item key="2">About</Menu.Item>
</Menu>
Putting NavLink inside the Menu.Item will allow you to switch from a nav tab to another.
<Menu
theme="dark"
mode="horizontal"
selectedKeys={[sideBarMenuKey]}
style={{ lineHeight: "64px" }}
>
<Menu.Item key="1">
<NavLink to="/">nav 1</NavLink>
</Menu.Item>
<Menu.Item key="2">
<NavLink to="/about">nav 1</NavLink>
</Menu.Item>
<Menu.Item key="3">nav 3</Menu.Item>
</Menu>
You can see it in action here

const [sideBarMenuKey, setSideBarMenuKey] = useState(1);
<Menu theme="dark" mode="inline" defaultSelectedKeys={[sideBarMenuKey]}>
<Menu.Item key="1" icon={<BirthIcon style={{ paddingRight: '10px' }} />}
onClick={ () => {
setSideBarMenuKey(1)
setShowBirth(true);
}
} >
Birth
</Menu.Item>
<Menu.Item key="2"
icon={<DeathIcon style={{ paddingRight: '10px' }} />}
onClick={ () => {
setSideBarMenuKey(2)
setShowBirth(false)
} }>
Death
</Menu.Item>
</Menu>
Using hooks look on to this

Related

Material UI Tabs with routes

So I'm using Material UI Tabs, and I'm trying when a user taps on tab it redirects him to a tab with a link but it would be still in the same main page
Example of what i'm looking for please notice the link changes as the tabs changes:
Material UI VerticalTabs
import * as React from 'react';
import PropTypes from 'prop-types';
import Tabs from '#mui/material/Tabs';
import Tab from '#mui/material/Tab';
import Typography from '#mui/material/Typography';
import Box from '#mui/material/Box';
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`vertical-tabpanel-${index}`}
aria-labelledby={`vertical-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
};
function a11yProps(index) {
return {
id: `vertical-tab-${index}`,
'aria-controls': `vertical-tabpanel-${index}`,
};
}
export default function VerticalTabs() {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Box
sx={{ flexGrow: 1, bgcolor: 'background.paper', display: 'flex', height: 224 }}
>
<Tabs
orientation="vertical"
variant="scrollable"
value={value}
onChange={handleChange}
aria-label="Vertical tabs example"
sx={{ borderRight: 1, borderColor: 'divider' }}
>
<Tab label="Item One" {...a11yProps(0)} />
<Tab label="Item Two" {...a11yProps(1)} />
<Tab label="Item Three" {...a11yProps(2)} />
</Tabs>
<TabPanel value={value} index={0}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
</Box>
);
}
I know that I have to use react-router-dom but I didn't know how to implement, any help would be appreciated.
Create a route component or destructive the routes in App.js. Just install react-router-dom and use BrowserRouter as Route as the Parent tag. Then add routes and structure your routes for your pages there.
import {BrowserRouter as Router, Routes, Route} from 'react-route-dom'
<Router>
<Routes>
<Route path='/pageName' element={<pageName/>}>
<Route path='/pageName2' element={<pageName2/>}>
</Routes>
</Router>
Also refer react router v6 docs. This playlist would helpful to using different routing mechanism. In your case you need to watch the tutorial 8 to do the nested routes. If you're a person who familiar with v5 and want to know the changes in v6, then refer this video also.

Scrolling the page up in React

In my component, i wanna scroll the page up when the user click on Link Component witch will change the url and therefore what user sees.
import { Link } from "react-router-dom";
const RelatedMovieItem = props => {
const movie = props.movie;
return (
<div className="card" style={{width: "18rem",marginTop: "10px", padding: 0, marginLeft: "10px"}}>
<img src={movie.imageUrl} className="card-img-top" alt={movie.name} />
<div className="card-body">
<h5 className="card-title">{movie.name}</h5>
<p className="card-text">{movie.description}</p>
<Link to={`/movies/${movie.id}`} className="btn btn-primary">Go to details</Link> //once this clicked i want to scroll the page up
</div>
</div>
);
};
export default RelatedMovieItem;
How to do so?
if every route changes you want to scroll top you can do it using <ScrollToTop> passing children down the tree like that.
<BrowserRouter>
<ScrollToTop>
<Switch>
// your route
</Switch>
</ScrollToTop>
</BrowserRouter>;
And create component ScrollToTop.js
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop({children}) {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return <>{children}</>;
}
You can make use of window.scrollTo(0,0) by passing it to the onClick attribute.
This way your page will scroll to the top left whenever you click on <Link />.
<Link
to={`/movies/${movie.id}`}
className="btn btn-primary"
onClick={() => window.scrollTo(0, 0)}
>
Go to details
</Link>
you can use a reference and attach it to the component you want to scroll to.
Then onclick you can call ref.current.scrollIntoView()

Can't navigate to URL param using nested routes

I built a small example of nested routes using the useRoutes hook. I don't understand what I am doing different than the examples.
Here's a codesandbox.
I am unable to navigate to the :customerId URL param while in the 'customers' route.
import React from "react";
import { BrowserRouter, Link, RouteObject, useRoutes } from "react-router-dom";
const contentRoutes: RouteObject[] = [
{
element: <div>Home Page</div>,
index: true
},
{
element: (
<div>
Customers <Link to="microsoft">Microsoft</Link> (this is the link that doesn't work)
</div>
),
path: "customers",
children: [
{
path: ":customerId",
element: <div>Customer Microsoft</div>
}
]
}
];
const Content: React.FC = () => {
const content = useRoutes(contentRoutes);
return <div style={{ display: "flex" }}>{content}</div>;
};
export default function App() {
return (
<BrowserRouter>
<div className="App">
<div style={{ display: "flex", gap: "10px", marginBottom: '20px' }}>
<Link to="/">Home</Link>
<Link to="customers">Customers</Link>
</div>
<Content />
</div>
</BrowserRouter>
);
}
Looks like I haven't completely understood the concept of Outlet.
In the examples showing the Routes and Route React components, they use the Outlet component to display children routes.
In my example, I should just change to:
<div>
Customers <Link to="microsoft">Microsoft</Link>
<Outlet /> // HERE, use Outlet to paint children routes.
</div>

How to Change footer value in antd layout when i click on button inside route based component in reactjs [duplicate]

This question already exists:
How to Change footer value inside antd layout when i click on button inside any route based component in reactjs
Closed 1 year ago.
I have used antd Layout with footer for Reactjs application. How to change value of footer when I click on button inside numbers component. I have used antd layout like this
https://ant.design/components/layout/
Code For Above Functionality Like:
When i click on button how to display Input value on Footer Label.
Mainly Two component Like :
WebSiteLayout.js (Footer inside them)
NumberList.js (Button and Input value inside them)
How to pass value from NumberList.js component to WebSiteLayout.js
WebSiteLayout.js
import React from "react";
import { withRouter, Switch, Link } from "react-router-dom";
import NumbersList from "../pages/Numbers/NumbersList";
import AuthenticatedRoute from '../../AuthenticatedRoute';
import './WebLayout.css';
import '../../../../assets/css/app.css';
import { Layout, Button, Menu, Popover, Spin, Select, Checkbox, Row, Col, Form, notification } from 'antd';
const { Header, Sider, Content, Footer } = Layout;
const { SubMenu } = Menu;
import {
PhoneOutlined,
} from '#ant-design/icons';
const WebSiteLayout = ({ location, row }) => {
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="topbar1">
<div className="topbar-left">
</div>
</div>
<Menu theme="dark" mode="inline">
<Menu.Item key="/numbers" icon={<PhoneOutlined style={{ fontSize: '18px' }} />} title="Numbers" >
<span>Numbers</span>
<Link to="/numbers" />
</Menu.Item>
</Menu>
</Sider>
<Layout className="site-layout" hasSider={true} style={{ height: "100vh" }}>
<Content
className="site-layout-background"
style={{
padding: 24,
height: '80px',
minHeight: 280,
}}
>
<Switch>
<AuthenticatedRoute path='/numbers' component={NumbersList} />
</Switch>
<Footer style={{ position: "fixed", bottom: "0px", right: "0px", background: "#c2c2c2", padding: "15px 25px" }}>
Display Numbers Field Value
</Footer>
</Content>
</Layout>
</Layout>
);
};
export default withRouter(WebSiteLayout)
NumberList.js
import React, {Component} from 'react';
import 'antd/dist/antd.css';
import { withRouter } from "react-router";
import { Button, Input } from 'antd';
class NumbersList extends Component {
constructor(props) {
super(props);
this.state = {
value:''
}
}
sendValueToFooter = () => {
console.log(this.state.value);
}
render() {
return (
<div class="container-fluid">
<h4 class="page-title float-left">Numbers</h4>
<Input placeholder="Basic usage" value={this.state.value} onChange={(e)=>{this.setState({value : e.target.value})}}/>
<br></br>
<Button type="primary" onClick={this.sendValueToFooter}>
Click Here To Send Value On Footer
</Button>
</div>
)
}
}
export default withRouter(NumbersList)
When I click on button how to display Input value on Footer Label.

react-router-dom+antD/ How to set a Nav Element Active as per the current route

I Created the following NavBar
import React,{Component} from "react";
import { Layout, Menu ,Icon} from 'antd';
import {NavLink} from 'react-router-dom';
const { Header } = Layout;
const SubMenu = Menu.SubMenu;
class NavBar extends Component {
constructor(props){
super(props);
this.state={
current: '1'
}
this.handleMenuClick=this.handleMenuClick.bind(this);
}
handleMenuClick(e){
this.setState({
current : e.key
})
}
render(){
return(
<Layout className="layout">
<Header>
<div className="logo" />
<Menu
theme="light"
mode="horizontal"
style={{ lineHeight: '64px' }}
selectedKeys={[this.state.current]}
onClick={this.handleMenuClick}
>
<Menu.Item key="1"><NavLink to="/">Home</NavLink></Menu.Item>
<Menu.Item key="2"><NavLink to="/about">About Us</NavLink></Menu.Item>
<Menu.Item key="3"><NavLink to="/contact">Contact Us</NavLink></Menu.Item>
<SubMenu key="4" title={<span><Icon type="setting" />Account</span>}>
<Menu.Item key="setting:1"><NavLink to="/login">Sign In</NavLink></Menu.Item>
<Menu.Item key="setting:2"><NavLink to="/register">Register</NavLink></Menu.Item>
</SubMenu>
</Menu>
</Header>
</Layout>
);
}
}
export default NavBar;
and its active state works fine while navigating from one state to another.But as soon as I hit the refresh button ,despite of what route it is currently on , It always sets the Home Nav Active. How can I set the nav element active dynamically, even if page gets refreshed.
You are setting the
current: '1'
Instead you should set the key based on the menu selected, If you have different endpoints for urls, you can get the url and based on the url you can modify the current variable.
If you don't have different url for different menu, you can save the current state using localstorage and can get the state from localstore agaain

Resources