I have sidebar with buttons, when i click on one i want to set color on selected button but it doesn't work properly because sometimes it requires clicking twice to set the color i don't know why. When i set setSelectedIndex(1) but in console.log(selectedIndex) shows 0
import { Drawer, List, ListItem, ListItemButton, ListItemText, styled, Toolbar } from '@mui/material'
import React, { useState } from 'react'
import { useNavigate } from 'react-router-dom';
import { sideItems } from './SideItems';
const Sidebar: React.FC = () => {
const drawerWidth = 240;
const navigate = useNavigate();
const [selectedIndex, setSelectedIndex] = useState(0);
const StyledListText = styled(ListItemText)({
textAlign: 'center',
color: 'white'
});
const CustomListButton = styled(ListItemButton)({
"&.Mui-selected": {
backgroundColor: "#2e8b57!important"
}
});
const handleListItemClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>, path: string, index: number) => {
event.preventDefault();
navigate(path);
setSelectedIndex(index);
}
return (
<Drawer
variant="permanent"
sx={{
width: drawerWidth,
[`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box' }
}}
PaperProps={{
elevation: 12,
sx: {
width: 240,
backgroundColor: "primary.light"
}
}}
>
<Toolbar />
<List>
{sideItems.map((item, index) =>
<ListItem key={item.id}>
<CustomListButton selected={index === selectedIndex} onClick={(event) => handleListItemClick(event, item.path, index)}>
<StyledListText primary={item.text} />
</CustomListButton>
</ListItem>
)}
</List>
</Drawer>
)
}
export default Sidebar
CodePudding user response:
Most likely here you are including <Sidebar/>
as child component inside the jsx of every page component so each time you are creating a new instance of it and since the initial selectedIndex
value is 0 it is always the first item which is selected no matter to which page component you have been redirected with navigate
, unless you click on another button to update selectedIndex
value of this new Sidebar
instance.
What you should do it to use <Sidebar/>
on the top level to create only one instance of it, so it does not lose it's hooks
values something like this :
function Layout(props) {
return (
<Fragment>
<Siderbar/>
<main>{props.children}</main>
</Fragment>
);
}
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<Component {...pageProps} />
</Layout>
);
CodePudding user response:
You can write your question better:
- Don't need write in this example:
import { useNavigate } from 'react-router-dom';
import { sideItems } from './SideItems';
- You must in example write const sideItems
const sideItems = [
{
id: '1',
text: 'lalala',
path: 'lololo'
}
]
Solutions:
- You can write solution - boolean:
(change your strings)
const [selectedIndex, setSelectedIndex] = useState(true);
const handleListItemClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>, path: string, index: number) => {
event.preventDefault();
setSelectedIndex(!selectedIndex);
}
<CustomListButton selected={selectedIndex} onClick={(event) => handleListItemClick(event, item.path, index)}>
- You can write solution - number:
(change your strings)
const [selectedIndex, setSelectedIndex] = useState(0);
const handleListItemClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>, path: string, index: number) => {
event.preventDefault();
setSelectedIndex((prev) => {
return prev ? 0 : 1
});
}
<CustomListButton selected={!!selectedIndex} onClick={(event) => handleListItemClick(event, item.path, index)}>