Prevent child component from triggering parent's onClick - javascript

Using Material-ui in React, I want to have the <IconButton> to have one behavior with onClick (quickly add to the cart) and have the parent <GridListTile> to have a different onClick behavior (open the more info Dialog for that item).
My issue is that when I click on the <IconButton>, it does both actions at the same time (open the more info Dialog AND add to the cart.
Is there a way to have <IconButton> to not inherit the onClick from the parent component?
<GridListTile key={tile.title}>
<img
src={`/${tile.img}`}
alt={tile.title}
onClick={handleClickOpen()}
/>
<GridListTileBar
title={tile.title}
subtitle={<span>{tile.description}</span>}
onClick={handleClickOpen()}
actionIcon={
<Tooltip title="add to cart">
<IconButton
aria-label={`info about ${tile.title}`}
className={classes.icon}
// onClick={handleClickOpen()}
onClick={() => {
dispatch({ type: "cart-increment" });
}}
>
<AddShoppingCartIcon />
</IconButton>
</Tooltip>
}
/>
</GridListTile>

In the child handler, stop the event from propagating upward:
<IconButton
aria-label={`info about ${tile.title}`}
className={classes.icon}
onClick={(e) => {
e.stopPropagation();
dispatch({ type: "cart-increment" });
}}

Related

How to show/hide buttons onFocus/onBlur below the textarea and allow to click it?

I need to add two buttons below the input field.
One button will allow saving the input in the textarea, and another aborting.
The buttons should be displayed when the input is focused.
Currently, the problem is that when I click any of the buttons, the input gets blurred again and thus they disappear and the onClick event doesn't happen.
I use MUI and Formik.
Can anyone tell me how to fix it?
const [buttonClicked, setButtonClicked] = useState(false);
...
return (
...
<Box sx={inspectionWrapperStyles} mb={'0.25rem'}>
<MultiLineInput
name={fieldName}
noMargin
required={required}
label={label}
onFocus={() => setInputFocused(true)}
onBlur={() => setInputFocused(false)}
ref={inputRef}
/>
{inputFocused && (
<>
<IconButton
altText=""
onClick={() => {
console.log('Saved');
}}
>
<SaveIcon />
</IconButton>
<IconButton
altText=""
onClick={() => {
console.log('Aborted');
}}
>
<XCircleIcon />
</IconButton>
</>
)}
</Box>
...
)
Looks like currently as you click a button the focus of the input is lost, so they disappear. Maybe it's a good idea to check if the button is clicked and only then hide it, like this:
{inputFocused && !buttonClicked (
then in both button's onClick you can add:
onClick={() => {
console.log('Aborted');
setButtonClicked(true);
}}
only thing left is to make sure that we reset it when input is focused again:
onFocus={() => {
setInputFocused(true);
setButtonClicked(false);
}}
const [showButton, setShowButton] = useState(false); // add this
<MultiLineInput
name={fieldName}
noMargin
required={required}
label={label}
onFocus={() =>{
setInputFocused(true)
setShowButton(true) // add this
}}
onBlur={() => setInputFocused(false)}
ref={inputRef}
/>
{inputFocused && showButton && (
<>
<IconButton
altText=""
onClick={() => {
console.log('Saved');
setShowButton(false) // add this if you want to hide buttons
}}
>
<SaveIcon />
</IconButton>
<IconButton
altText=""
onClick={() => {
console.log('Aborted');
setShowButton(false) // add this if you want to hide buttons
}}
>
<XCircleIcon />
</IconButton>
</>
)}

How to disable ripple effect on primary action in react material lists

I was trying to have two action buttons on the left and right end of the list component.
on click of secondary action (right side delete icon) the ripple is limited to the only icon.
on click of primary action(left delete icon) the ripple effect is on the whole row.
Expected Behaviour :
I want the ripple effect on the primary, similar to that of the secondary action button.
And important I cannot disable the text ripple effect as temporary solution.
Code Sample:
Code-Sandbox link
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import ListItemSecondaryAction from "#material-ui/core/ListItemSecondaryAction";
import DeleteIcon from "#material-ui/icons/Delete";
import IconButton from "#material-ui/core/IconButton";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
}
}));
export default function SelectedListItem() {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
};
return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
<ListItem
button
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemIcon>
<ListItemText primary="Inbox" />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
<ListItem
button
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemIcon>
<ListItemText primary="Drafts" />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List>
</div>
);
}
I think it's because is used to add secondary action to button so when you click on the secondary action area it prevent primaryAction to happen. So in your case when you click on right icon it contains the ripple affect inside ListItemSecondaryAction area. If you want to disable ripple on the List you can add prop 'disableRipple' on your ListItem and it will be disabled but if you want it conditional ie. when user clicks on icon ripple should happen only on icon and if clicked on button only in button than you can try stop propagation when clicked on button ( might not work ) but you can give it a try.
I've created a work around sharing codesandbox link with you
https://codesandbox.io/s/material-demo-forked-i7k7e?file=/demo.js
<ListItem
button
disableRipple
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
style={{ position: "relative" }}
>
<div style={{ zIndex: 1 }}>
<ListItemIcon>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemIcon>
<ListItemText primary="Inbox" />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</div>
<ButtonBase
style={{
position: "absolute",
bottom: 0,
top: 0,
left: 0,
right: 0,
width: "100%",
zIndex: 0
}}
/>
</ListItem>

How to close drawer component from main page with menu button onclick on main page (React-js)

I'm new in reactjs, I have main page
<div >
<AppBar position="flex" style={{backgroundColor:'#1A437E'}}>
<Toolbar>
<IconButton edge="end" color="inherit" aria-label="menu">
<img alt="open menu" height="57" width="50" border="0" src={ico7} />
</IconButton>
</Toolbar>
</AppBar>
<Drower/>
</div>
And this is my Drawer
<div>
<Drawer anchor='right' open={this.state.open}>
<List>
<ListItem>
<ListItemIcon/>
</ListItem>
</List>
</Drawer>
</div>
How can I open and close the drawer?
Use below code for IconButton click
onClick={()=>{
this.setState(state => ({
open: !state.open
}));
}}
To open/close the Drawer you need to toggle the value of this.state.open in your state. Like so:
<div>
<button onClick={() => this.setState({ open: !this.state.open }))} >Open / Close Drawer </button>
<Drawer anchor='right' open={this.state.open}>
<List>
<ListItem>
<ListItemIcon/>
</ListItem>
</List>
</Drawer>
</div>

Disable button from clicking the parent element

I am building a Material UI app. I have a card component and I need to make it clickable. But there is also a button on the top right corner which edits the card on click. The problem is, when I click on that button, it'll handle 2 actions:
the edit action called on the button itself
clicks the card
Here is a snippet of code:
<CardActionArea href={`/${item.name}`}>
<Card key={index}>
<CardHeader
action={
<Fab color="secondary" onClick={handleClick}>
<EditIcon color={"primary"} />
</Fab>
}
/>
<CardContent className={classes.cardContent}>
<Avatar className={classes.avatar} alt={item.name} src={item.avatar} />
<Typography variant="h6" color="textSecondary">
Card description
</Typography>
</CardContent>
</Card>
</CardActionArea>
How could I achieve that?
In the card component you create both functions you want to run when clicking the card and the inner button:
const onCardClick = () => {
// your code here
}
const onButtonClick = () => {
onCardClick()
// your code here
}
In Card.jsx when rendering the button inside the card add the onButtonClick function
<MyButton onClick={onButtonClick}
then in your button component, you give it an onClick prop:
const MyButton = (props) => {
<Button onClick={prop.onClick}>title</Button>
}

IconButton onClick event kicks in onLoad react

I have the below IconButton getting displayed right next to each and every row so for each row I can do a different API call. These buttons' alert gets displayed onLoad?! How can I fix it?
<IconButton onClick={alert("abc")}>
<Edit color="action" />
</IconButton>
<IconButton onClick={alert("abc")}>
<Update color="action" />
</IconButton>
<IconButton onClick={alert("abc")}>
<Delete color="action" />
</IconButton>
You are calling the function in your onClick which is why it gets triggered onLoad
You can use the es6 arrow function
<IconButton onClick={() => alert("abc")}>
<Edit color="action" />
</IconButton>
<IconButton onClick={() => alert("abc")}>
<Update color="action" />
</IconButton>
<IconButton onClick={() => alert("abc")}>
<Delete color="action" />
</IconButton>

Categories

Resources