Child component updates parent state unexpectedly in react - reactjs

I'm using react with MUI. I'm using the MUI List component which each list may have a nested list in it. the problem is when I open a nested list, the parent component gets closed. I don't know what I am doing wrong. You could use this CodeSandbox.
Here is an image of my app: Gif

Your problem is you add onClick on ListItem which contains all other sub-items, so that means whenever you trigger click on any sub-item, you accidentally trigger the main ListItem too. That's why you've seen the main menu item gets collapsed when you click on any sub-item. You can read more about event propagation.
The easiest fix is you should move onClick={handleToggleCollapse} from ListItem to ListItemButton
<ListItem {...listItemProps} sx={{ display: "block" }}>
<ListItemButton onClick={handleToggleCollapse}>
{icon && <ListItemIcon>{icon}</ListItemIcon>}
<ListItemText primary={text} />
{open ? (
<IoIosArrowUp className="text-lg" />
) : (
<IoIosArrowDown className="text-lg" />
)}
</ListItemButton>
<Collapse in={open} timeout={300}>
<List disablePadding>
<ListItems listItems={nestedItems} />
</List>
</Collapse>
</ListItem>
For event propagation prevention, you should modify handleToggleCollapse like below
function handleToggleCollapse(e: SyntheticEvent) {
e.stopPropagation();
setOpen((prev) => !prev);
}
Sandbox

Related

Material-UI, ListItem child onClick listener, triggers the parent onClick too

Using the List component of MUI when I click a child component of ListItem (in this case the delete iconButton) the onClick callback of the ListItem it self is also triggered.
in the below example if I click the delete button beside the handleDelete the parent onClick callback (handleNavigate ) is also triggered. The same is for handleValidate of the avatar
Why is it so, and how to prevent it?
<List>
<ListItem
onClick={handleNavigate}
secondaryAction={
<IconButton edge="end" aria-label="delete" onClick={handleDelete}>
<DeleteIcon />
</IconButton>
}
>
<ListItemAvatar onClick={handleValidate}>
<Avatar>
<FolderIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Single-line item"
/>
</ListItem>
</List>
add Event.stopPropagation()
const handleDelete = (ev) => {
ev.stopPropagation()
// rest of the code
}

How to get a specific ListItem from Menu Component in MUI for React

I have a List that has several ListItems which I get from an array, let's call it myCollection. Each ListItem has a MenuIcon which opens a Menu, showing an option to delete the item. The simplified code looks like this:
<List>
<Menu
open={Boolean(anchorEl)}
anchorEl={anchorEl}
...>
<MenuItem onClick={() => handleDelete(⚡collectionItem)}>Delete</MenuItem>
</Menu>
{myCollection.map((collectionItem) => (
<ListItem secondaryAction={
<IconButton onClick={(e) => setAnchorEl(e.currentTarget)}>
<MoreVertIcon />
</IconButton>
}>
{collectionItem.name}
</ListItem>
)}
</List>
My problem is that I need to know which item in myCollection was selected to pass it to the delete function. However, I don't have a reference to the current collectionItem in the myCollection array from the Menu. Moving the Menu into the map function makes no sense because multiple menus would be rendered. How can I solve this problem?
I solved it by giving the IconButtons custom HTML data attributes, e.g.
<IconButton data-collection-id={collection.id} onClick={...}>
...
</IconButton>
Then, in the onClick function, we can retrieve it by using event.currentTarget.getAttribute("data-collection-id").

Unable to solve the Menu bar issue using reactjs?

I'm new to Framework and i have an issue with me sidebar which is showing Table tab as default but it should not show any tab's page as clicked. By default it should show nothing. When we click on a particular tab then it should show color on the tab of the sidebar. If i give -1 then the color is getting disappeared on the menu tab but the Tab's page remain as it is. Is it possible to give pathname. how to fix by giving id ? . If Yes Can anyone help me in fixing this issue?
Here is the code:
<Drawer
className={classes.drawer}
variant="permanent"
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.toolbar} />
<List>
{["table", "home"].map((item, index) => {
const Icon = itemsConfig[item].icon;
return (
<ListItem
component={Link}
to={itemsConfig[item].link}
selected={selectedIndex === index}
onClick={event => handleListItemClick(event, index)}
button
key={item}
>
<ListItemIcon>
<Icon />
</ListItemIcon>
<ListItemText primary={itemsConfig[item].text} />
</ListItem>
);
})}
</List>
</Drawer>
Here is whole code: "https://codesandbox.io/s/fervent-browser-ecz7z"
Can anyone help me in this ?
Define your state, that holds the selected index in your <Layout /> component. Define function that manipulate that state and pass it to , <Home /> andcomponents along with the newly defined state. Finally call the function after the initial rendering of the component. For the (class component) use:
componentDidMount() {
this.props.setTabIndex(this.props.index);
}
For the <Table /> use:
useEffect(() => {
props.setTabIndex(props.index);
}, []);
The full result is posted here: https://codesandbox.io/s/cold-mountain-hlhk8?

Material UI List scroll doesn't reset when list data changes

I have a simple list of items.
<List>
{this.state.initialList.map((item, index) => (
<ListItem key={index} button>
<ListItemText primary={item} />
</ListItem>
))}
</List>
If I scroll to the bottom of the list and load a new list, the scroll stays at the bottom. How do I scroll back to the top of the list?
https://codesandbox.io/s/material-demo-l4i3s
In this sandbox, scroll to the bottom, click on "load", the list is showing the last item of the new list.
Not sure if there's a default setting you can set with material-ui to do this. But you definitely can apply some React functionality to get this to work.
See working sandbox: https://codesandbox.io/s/material-demo-8ftqz
Create a ref which would point to the top of the list:
topOfList = React.createRef();
Then in your List Component, return a span at the top of the list. Give that span the ref you crated.
<List component="nav" aria-label="secondary mailbox folders">
{<span ref={this.topOfList} />}
{this.state.initialList.map((item, index) => (
<ListItem key={index} button>
<ListItemText primary={item} />
</ListItem>
))}
</List>
Create an additional handler that scrolls to the element/ref:
scrollToTop = () => {
if (this.topOfList.current) {
this.topOfList.current.scrollIntoView();
}
};
Lastly, use the handler inside the set-state call-back to ensure you scroll-up after the data has loaded.
this.setState({ initialList: newList }, () => this.scrollToTop())
The scroll is caused by the parent div.
You can hold a ref to that div, and on load, use the function scorllTo() to position the scrollbar to the top.
<div ref={ref => this.divRef = ref} class="mylist">
<List component="nav" aria-label="secondary mailbox folders">
{this.state.initialList.map((item, index) => (
<ListItem key={index} button>
<ListItemText primary={item} />
</ListItem>
))}
</List>
</div>
and than in load function:
load = () => {
const newList = [
"new1",
"new2",
"new3",
"new4",
"new5",
"new6",
"new7",
"new8",
"new9",
"new0"
];
this.divRef.scrollTo(0,0);
this.setState({ initialList: newList });
};
You can refer to this CodeSandbox example

How will prevent triggering navLink/link in react router when click right icon button with nested list item? Material UI

I have a sample code here: ListItem with nested list I want to prevent the link and trigger the nested list. Or I want when I click the rightIconButton only triggers the nested list not the link.
<ListItem
value={3}
primaryText="My Collection"
leftIcon={<Avatar src="http://i.imgur.com/fmvLZGS.png" />}
initiallyOpen={false}
containerElement={<NavLink to="/CollectionPage" />}
nestedItems={[
<ListItem
value={4}
key={1}
primaryText="Wines"/>,
<ListItem
value={5}
key={2}
primaryText="Arts"/>,
<ListItem
value={6}
key={3}
primaryText="Vehicles"/>
]} />
Thank you!

Resources