I have a problem because my component keeps rerendering while I'm toggling it.
I have an arrow(up/down) that will show/hide a part of the div.
I wrote console.log('render') to see if its rerendering. I'm only toggling one div at a time but it is rerendering multiples times. How do I fix this?
CODESANDBOX --------> CODESANDBOX
const toggleArrow = (
<IconButton
aria-label="delete"
size="large"
onClick={() => {
handleProductCard(productIndex);
productCardRef.current.scrollIntoView({
behavior: "smooth"
});
}}
sx={{ padding: 0 }}
>
{formik.values.hideProductCard ? (
<KeyboardArrowDownIcon size="large" />
) : (
<KeyboardArrowUpIcon size="large" />
)}
</IconButton>
);
Related
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>
</>
)}
I need help to add a feature like if the user clicked on the button it changes the color to danger of a button so it would feel like it is selected and if the user clicks the same button it should come back to the original color like primary.
I want to make it a little dynamic as well so it should know which button is being selected in the mapped objects.
I have the following code for now.
<Row xs={3} md={4} className="g-3 py-2 px-2">
{data.map((postData) => {
return (
<Button
className="mx-2"
style={{
width: "10rem",
height: "13rem",
lineHeight: "14px",
fontSize: "12px"
}}
key={postData.id}
>
<Card.Img variant="top" src={postData.image} />
<Card.Body>
<Card.Title className={style.tile}>
{postData.title}
</Card.Title>
</Card.Body>
</Button>
);
})}
</Row>
It's also running in the CodeSandBox
If you can give me a little idea of how to approach this?
I tried using useState for that purpose
onClick={() => setCls((cls) => (cls === "red" ? "green" : "red"))
But that changes the color of all the buttons at one click, you can see this in codesandbox.
try this Code :
import React, { useState } from "react";
//react bootstrap components
import { Container, Row, Card, Button } from "react-bootstrap";
//scss
import style from "./styles.module.scss";
//data for post
import data from "./data.json";
const App = () => {
const [selectedBtnList, setSelectedBtnList] = useState([])
const selectBtnAddHandler = (btnId) => {
const isInList = selectedBtnList.some(item => item === btnId)
if(isInList){
setSelectedBtnList(selectedBtnList.filter(item => item !== btnId))
}else {
setSelectedBtnList([...selectedBtnList,btnId])
}
}
return (
<>
<Container fluid={true}>
<Row xs={3} md={4} className="g-3 py-2 px-2">
{data.map((postData) => {
return (
<Button
className="mx-2"
style={{
width: "10rem",
height: "13rem",
lineHeight: "14px",
fontSize: "12px"
}}
key={postData.id}
variant={selectedBtnList.includes(postData.id) ? "danger" : "primary"}
onClick={()=> selectBtnAddHandler(postData.id)}
>
<Card.Img variant="top" src={postData.image} />
<Card.Body>
<Card.Title className={style.tile}>
{postData.title}
</Card.Title>
</Card.Body>
</Button>
);
})}
</Row>
</Container>
</>
);
};
export default App;
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>
I have this render function:
render() {
const {classes} = this.props
return (
<Paper className={classes.root} elevation={4}>
<Typography type="title" className={classes.title}>
All Users
</Typography>
<List dense>
{this.state.users.map((item, i) => {
const photoUrl = item._id
? `/api/users/photo/${item._id}?${new Date().getTime()}`
: '/api/users/defaultphoto'
return <Link to={"/user/" + item._id} key={i}>
<ListItem button>
<ListItemAvatar>
<Avatar src={photoUrl} className={classes.bigAvatar}/>
</ListItemAvatar>
<ListItemText primary={item.name}/>
<ListItemSecondaryAction>
<IconButton>
<ArrowForward/>
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</Link>
})
}
</List>
</Paper>
)
}
}
When I Initially load the page the below blue dot appears to the left of each user:
If I then click a link to another page and then the browser back arrow to return to the Users page the user list appears without the blue dot:
I would prefer if the blue dot never appears. How do I do this?
I faced the same problem today using ListItem component from react material ui v4.6.0.
The default list style type value of disc will be displayed if "ListItemSecondaryAction" component is used as the last child of "ListItem" component.
According to the React Material UI Docs, the value of ContainerComponent prop will be set to 'li' when ListItemSecondaryAction is used as the last child.
Changing the value of ContainerComponent prop to other elementType such as 'div' fixed the issue.
<ListItem button ContainerComponent="div">
<ListItemAvatar>
<Avatar src={photoUrl} className={classes.bigAvatar} />
</ListItemAvatar>
<ListItemText primary={item.name} />
<ListItemSecondaryAction>
<IconButton>
<ArrowForward />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
I assume somewhere in the nested components you have a li of some sort.
The dots are from html list bullet points and can be removed by adding the following css:
ul {
list-style-type: none !important;
}
If the component is a part of the Material-UI library, you might need to add !important
My use-case is trigger something when click parent component (TouchableOpacity for example), but trigger nothing when click children component (Screen and others for Example). My case is same like prevent bubbling on web. From docs I've read said that it should use onStartShouldSetResponderCapture, but I still don't understand for the usage. Below is the snippet.
<TouchableOpacity style={styles.container} onPress={() => alert('tes')}
>
<Screen style={styles.screenContainer} onStartShouldSetResponderCapture={() => false}>
<Title>Edit Nama Restoran</Title>
<Divider styleName='line' />
<TextInput
style={{ flex: 1 }}
value={value}
onChangeText={value => this.setState({ value })}
onSubmitEditing={this.handleSubmit}
/>
<View styleName='horizontal' style={styles.nameContainer}>
<Button styleName='confirmation dark' onPress={this.handleSubmit}>
<Text>UPDATE</Text>
</Button>
<Button styleName='confirmation' onPress={onClose}>
<Text>CLOSE</Text>
</Button>
</View>
</Screen>
</TouchableOpacity>
EDIT:
Below is an illustration of my case.
If I click overlay (outside of <Screen>), it triggers an alert (expected).
If I click white dialog (<Screen> and children inside), it triggers an alert too (unexpected). What I need is not trigger alert when click <Screen>.
Updated to show how to do with a modal:
<Modal
visible={this.state.modalVisible}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<TextInput
style={{ flex: 1 }}
value={value}
onChangeText={value => this.setState({ value })}
onSubmitEditing={this.handleSubmit}
/>
</Modal>
Not sure if this is the right snippet from your code, but basically the part you want to pop up, pull it out from the touchable opacity and wrap a modal around it. Then on click have it visible/invisible.