Components do not re-render on state change react - javascript

I'm stuck with this annoying problem, in which, I'm just trying to make a navigation bar containing a hamburger and stuff, like that, but components do not re-render or update when I click on a button which is supposed to change the state of the components, and cause the hamburger button and menu to open and vice versa.
import React, {useState} from "react";
import styles from "./Navigation.module.css";
import pin from "./pin.png";
import "./NavFunctions";
function Navigation(props) {
let [country, setCountry] = useState("UK");
let [city, setCity] = useState("London");
let [response, setResponse] = useState({});
let [MenuHamburgerToggled, setMenuHamburgerToggled] = useState(false);
const style = {
nav: {
position: "absolute",
top: "0",
left: "0",
width: "100vw",
height: "150px",
backgroundColor: "transparent"
},
nav_container: {
flexDirection: "row",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
margin: "40px 100px",
color: "white",
},
cnc: {
fontFamily: '"Agency FB", serif',
fontSize: '30pt',
},
hamburgerMenuToggle: {
width: "30px",
height: "30px",
}
}
return (
<React.Fragment>
<Menu isOpen={MenuHamburgerToggled}/>
<nav style={style.nav}>
<div style={style.nav_container}>
<div style={style.cnc}>
{country}, {city} <img style={{
width: "30px", height: "30px", marginLeft: "10px"
}} src={pin} alt="pin"/>
</div>
<div style={style.hamburgerMenuToggle}>
<MenuHamburger toggled={MenuHamburgerToggled} onClick={()=> setMenuHamburgerToggled(!MenuHamburgerToggled)}
/>
</div>
</div>
</nav>
</React.Fragment>
)
}
function MenuHamburger(props) {
let [toggled, setToggled] = useState(props.toggled ? props.toggled : false);
const handleClick = () => {
setToggled(!toggled);
}
const styles = {
toggleHamburger: {
zIndex: "1001",
cursor: "pointer",
transition: "all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggleHamburger__ic: {
display: "inline-block",
cursor: "pointer",
},
tg__hr: {
backgroundColor: "transparent",
border: "none",
outline: "none",
transition: "all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggle_bir: {
width: "30px",
height: "3px",
borderRadius: "3px",
backgroundColor: "#efecff",
margin: "4px 0",
transform: toggled ? "rotate(-45deg) translate(-7px, 2px)" : "none",
WebkitTransform: toggled ? "rotate(-45deg) translate(-7px, 2px)" : "none",
transition: " all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggle_iki: {
width: "30px",
height: "3px",
borderRadius: "3px",
backgroundColor: "#efecff",
margin: "4px 0",
opacity: toggled ? "0" : "1",
transition: " all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggle_och: {
width: "30px",
height: "3px",
borderRadius: "3px",
backgroundColor: "#efecff",
margin: "4px 0",
transform: toggled ? "rotate(45deg) translate(-7px, -5px)" : "none",
WebkitTransform: toggled ? "rotate(45deg) translate(-7px, -5px)" : "none",
transition: " all cubic-bezier(.51,.58,.28,.98) .25s",
},
}
return (
<div style={styles.toggleHamburger}
>
<button style={styles.tg__hr} >
<div style={styles.toggleHamburger__ic}>
<div style={styles.toggle_bir}></div>
<div style={styles.toggle_iki}></div>
<div style={styles.toggle_och}></div>
</div>
</button>
</div>
)
}
function Menu(props) {
let [isOpen, setIsOpen] = useState(props.isOpen ? props.isOpen : false);
let styles = {
Menu_container: {
width: "100vw",
height: isOpen === true ? "100vh" : "0",
backgroundColor: "#211113",
transition: "all ease 1s"
}
}
return (
<div style={styles.Menu_container}>
</div>
)
}
export default Navigation;

Your MenuHamburger is not an html element component, so onClick won't work if you don't manually call it.
What you can do is pass the onClick into your div inside the MenuHamburger render method. This will ensure that when the user clicks the div, it will call the onClick on the <MenuHamburger onCLick={...} parent.
function MenuHamburger(props) {
...
return (
<div style={styles.toggleHamburger} onClick={props.onClick}>
...
}

Finally, I managed to fix the problem by adding useEffect like this:
function MenuHamburger(props) {
let [toggled, setToggled] = useState(props.toggled ? props.toggled : false);
const handleClick = () => {
setToggled(!toggled);
}
useEffect( () => {
setToggled(props.toggled);
}, [props.toggled]);
const styles = {
toggleHamburger: {
zIndex: "1001",
cursor: "pointer",
transition: "all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggleHamburger__ic: {
display: "inline-block",
cursor: "pointer",
},
tg__hr: {
backgroundColor: "transparent",
border: "none",
outline: "none",
transition: "all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggle_bir: {
width: "30px",
height: "3px",
borderRadius: "3px",
backgroundColor: "#efecff",
margin: "4px 0",
transform: toggled ? "rotate(-45deg) translate(-7px, 2px)" : "none",
WebkitTransform: toggled ? "rotate(-45deg) translate(-7px, 2px)" : "none",
transition: " all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggle_iki: {
width: "30px",
height: "3px",
borderRadius: "3px",
backgroundColor: "#efecff",
margin: "4px 0",
opacity: toggled ? "0" : "1",
transition: " all cubic-bezier(.51,.58,.28,.98) .25s",
},
toggle_och: {
width: "30px",
height: "3px",
borderRadius: "3px",
backgroundColor: "#efecff",
margin: "4px 0",
transform: toggled ? "rotate(45deg) translate(-7px, -5px)" : "none",
WebkitTransform: toggled ? "rotate(45deg) translate(-7px, -5px)" : "none",
transition: " all cubic-bezier(.51,.58,.28,.98) .25s",
},
}
return (
<div style={styles.toggleHamburger}
onClick={props.onClick}
>
<button style={styles.tg__hr} >
<div style={styles.toggleHamburger__ic}>
<div style={styles.toggle_bir}></div>
<div style={styles.toggle_iki}></div>
<div style={styles.toggle_och}></div>
</div>
</button>
</div>
)
}
function Menu(props) {
let [isOpen, setIsOpen] = useState(props.isOpen ? props.isOpen : false);
useEffect( () => {
setIsOpen(props.isOpen);
}, [props.isOpen]);
let styles = {
Menu_container: {
width: "100vw",
height: isOpen === true ? "100vh" : "0",
backgroundColor: "#211113",
transition: "all ease 1s"
}
}
return (
<div style={styles.Menu_container}>
</div>
)
}
So, now components update as their state changes, and animations work fine too.
thanks for the guide.

Related

How can I animate a hidden card to appear

I have a set of MUI cards. One of these cards is hidden until the other cards are expanded. When the cards expand, the hidden card then appears. I want to animate the hidden card so that it does not suddenly pop in.
** Styling **
const useStyles = makeStyles((theme) => ({
summaryBox: {
display: "flex",
},
qCard: {
backgroundColor: "#D9F5FD",
border: "1px solid #E1E1E1",
width: "5vw",
height: "auto",
marginRight: "1vw",
animation: "pop 500ms ease-in-out forwards",
},
expand: {
transform: "rotate(0deg)",
marginLeft: "auto",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shortest,
}),
},
expandOpen: {
transform: "rotate(180deg)",
},
}));
...
const [expanded, setExpanded] = useState(false);
const [hidden, setHidden] = useState(true);
const handleExpandClick = () => {
setExpanded(!expanded);
setHidden(!hidden);
};
...
<Box className={classes.summaryBox} onClick={handleExpandClick} aria-expanded={expanded}>
<Card className={classes.qCard} hidden={hidden}>
<Collapse in={expanded} timeout="auto" unmountOnExit>
</Collapse>
</Card>
</Box>
I can suggest of use the easing prop on the collapse component.
https://mui.com/material-ui/api/collapse/
Try adding this css,
qCard: {
backgroundColor: "#D9F5FD",
border: "1px solid #E1E1E1",
width: 0,
height: 0,
marginRight: "1vw"
},
animate: {
animation: "$myEffect 500ms ease-in-out forwards"
},
"#keyframes myEffect": {
"0%": {
width: 0,
height: 0,
},
"100%": {
width: "5vw",
height: "auto"
}
}
Dynamically add the animate style based on hidden value.

How can I change the color of MUI styled component?

I have a MUI styled component that renders a green circular badge.
const StyledGreenBadge = styled(Badge)(({ theme }) => ({
'& .MuiBadge-badge': {
backgroundColor: '#44b700',
color: '#44b700',
width: '15px',
height: '15px',
borderRadius: '100%',
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
'&::after': {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: '50%',
animation: 'ripple 1.2s infinite ease-in-out',
border: '1px solid currentColor',
content: '""',
},
},
'#keyframes ripple': {
'0%': {
transform: 'scale(.8)',
opacity: 1,
},
'100%': {
transform: 'scale(2.4)',
opacity: 0,
},
},
}));
Now, I want my code to be DRY, so I want to create a StyledYellowBadge.
All I have to do is somehow just change the color property of StyledGreenBadge.
Yet, I could not figure out how for 3 hours.
I have tried something like this:
color: { desiredColor === 'yellow' ? 'yellow' : #44b700'},
where desiredColor is a second argument, after
{ theme }
How can I make achieve this?
You can add custom properties to your styled MUI component by describing the type:
const StyledGreenBadge = styled(Badge)<{ badgeColor?: string }>(
Then, you can pass described property (badgeColor in this case) to your styled Badge component:
<StyledGreenBadge badgeColor="red" badgeContent={4} color="primary">
and assign it to the property you want:
backgroundColor: props.badgeColor ?? "#44b700",
Full code:
const StyledGreenBadge = styled(Badge)<{ badgeColor: string }>(
({ theme, ...props }) => {
console.log(props);
return {
"& .MuiBadge-badge": {
backgroundColor: props.badgeColor ?? "#44b700",
color: "#44b700",
width: "15px",
height: "15px",
borderRadius: "100%",
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
"&::after": {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
borderRadius: "50%",
animation: "ripple 1.2s infinite ease-in-out",
border: "1px solid currentColor",
content: '""'
}
},
"#keyframes ripple": {
"0%": {
transform: "scale(.8)",
opacity: 1
},
"100%": {
transform: "scale(2.4)",
opacity: 0
}
}
};
}
);
export default function SimpleBadge() {
return (
<StyledGreenBadge badgeColor="red" badgeContent={4} color="primary">
<MailIcon color="action" />
</StyledGreenBadge>
);
}
Demo

How to overlay the expand of accordion?

I would like to overlay the expand of the accordion but I don't know whoch part should I do. in the first picture show that output I want it overlay the other textfield when clicking the dropdown icon, as for the second picture show my current output which every time I click the dropdown it does not overlay rather it adjust the textfield. any advice?
What I want:
My current output:
my code:
<Accordion expanded={isExpanded} onClick={() => setExpanded(!isExpanded)}>
<AccordionSummary
expandIcon={<Icon icon={IMAGE.DropIcon} classStyle={classes.icon} />}>
{changeType}
</AccordionSummary>
<AccordionDetails>
<Button
className={classes.button}
onClick={() => onChangeType('GOLD')}>
GOLD
</Button>
<Button
className={classes.button}
onClick={() => onChangeType('GEMS')}>
GEMS
</Button>
<Button
className={classes.button}
onClick={() => onChangeType('COINS')}>
COINS
</Button>
</AccordionDetails>
</Accordion>
my styles:
const Accordion = withStyles({
root: {
padding: 0,
marginBottom:'1rem',
borderRadius: '8px',
boxShadow: 'none',
minHeight: '2em',
width: '15vw',
fontSize: '1.5em',
fontWeight: 600,
'&:not(:last-child)': {
borderBottom: 0,
},
'&:before': {
display: 'none',
},
'&$expanded': {
minHeight: '2em',
padding: 0,
marginTop: 2,
marginBottom: 2,
},
},
expanded: {},
})(MuiAccordion);
const AccordionSummary = withStyles({
root: {
margin: 0,
padding: '0 0 0 1em',
display: 'flex',
flexDirection: 'row',
borderRadius: '8px',
border: '1px solid',
minHeight: '2em',
width: '13.7vw',
'&$expanded': {
minHeight: '2em',
},
'& .MuiAccordionSummary-expandIcon.Mui-expanded': {
transform: 'none',
},
'& .MuiAccordionSummary-expandIcon': {
transform: 'none',
transition: 'none',
},
'& .MuiIconButton-edgeEnd': {
margin: 0,
padding: 0,
},
},
content: {
margin: 0,
'&$expanded': {
margin: 0,
},
},
expanded: {},
})(MuiAccordionSummary);
const AccordionDetails = withStyles(() => ({
root: {
padding: '0',
display: 'flex',
flexDirection: 'column',
width: '100%',
},
}))(MuiAccordionDetails);
You can add overlay by adding z-index to the AccordionDetails.
<Accordion >
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography>Accordion 1</Typography>
</AccordionSummary>
<div style={{zIndex:1 ,position:'absolute' ,width:'100%'}}>
<Card >
<AccordionDetails>
<Button>GOLD</Button>
<Button>GEMS</Button>
<Button>COINS</Button>
</AccordionDetails>
</Card>
</div>
</Accordion>
{/* Other contents of the page */}
hey I was looking to Accadian docs https://mui.com/components/accordion/
and all you need to do is just add some for AccordionDetails like background color , and to match the image some padding at the bottom, border and border radius and I tried this for demonstrate this here https://stackblitz.com/edit/react-k3tktv?file=demo.js

Create common style classes for JSS in material-ui

I'm using material-ui's JSS implementation to styling classes.
I have a lot of duplicated code when it comes to the components' styles since I have separated my components.
For example, I have cards which all use common styling:
const styles = theme => ({
cardContainer: {
position: 'relative',
width: '50%',
padding: theme.spacing.unit / 2,
},
cardOuter: {
height: '100%',
width: '100%',
textAlign: 'start',
},
card: {
width: '100%',
background: theme.palette.backgrounds.card.off,
},
cardOn: {
background: theme.palette.backgrounds.card.on,
},
cardUnavailable: {
background: theme.palette.backgrounds.card.disabled,
},
cardContent: {
display: 'flex',
flexWrap: 'wrap',
minHeight: 98,
height: 98,
[theme.breakpoints.down('sm')]: {
minHeight: 74,
height: 74,
},
padding: `${theme.spacing.unit * 1.5}px !important`,
},
});
which I would only rarely want to extend upon the styles inside the component, but would like to import these objects into an existing styles function so I do not have to duplicate these objects.
Has anyone or does anyone know how to do this?
Thanks
Figured it out. For future viewers:
const styles = theme => ({
...card(theme),
grid: {
height: '100%',
width: 'fit-content',
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 2,
flexWrap: 'nowrap',
overflowY: 'hidden',
},
name: {
overflow: 'hidden',
textOverflow: 'ellipsis',
fontSize: '1.12rem',
fontColor: theme.palette.text.main,
[theme.breakpoints.down('sm')]: {
fontSize: '0.9rem',
}
},
state: {
textOverflow: 'ellipsis',
margin: '0 auto',
marginTop: theme.spacing.unit / 2,
fontSize: '1.0rem',
fontColor: theme.palette.text.light,
[theme.breakpoints.down('sm')]: {
fontSize: '0.8rem',
}
},
alarmArmedHome: {
background: theme.palette.backgrounds.card.alarm.home,
},
alarmArmedAway: {
background: theme.palette.backgrounds.card.alarm.away,
},
alarmTriggered: {
background: theme.palette.backgrounds.card.alarm.triggered,
},
icon: {
margin: '0 auto',
color: theme.palette.text.icon,
fontSize: '2.7rem',
[theme.breakpoints.down('sm')]: {
fontSize: '1.7rem',
}
},
});
card.js
const styles = (theme) => ({
cardContainer: {
position: 'relative',
width: '50%',
padding: theme.spacing.unit / 2,
},
cardOuter: {
height: '100%',
width: '100%',
textAlign: 'start',
},
card: {
width: '100%',
background: theme.palette.backgrounds.card.off,
},
cardOn: {
background: theme.palette.backgrounds.card.on,
},
cardUnavailable: {
background: theme.palette.backgrounds.card.disabled,
},
cardContent: {
display: 'flex',
flexWrap: 'wrap',
minHeight: 98,
height: 98,
[theme.breakpoints.down('sm')]: {
minHeight: 74,
height: 74,
},
padding: `${theme.spacing.unit * 1.5}px !important`,
},
});
export default styles;
So you pretty much have to join the objects passing in the theme if required:
...card(theme),

Trouble centering items within parents in React

I’m having trouble aligning two components within another div in React. I’m using relative positioning for the parent div (snippetButtonHolder) and absolute positioning for its children (snippet and button). I want snippet to be centered in the parent and button to be under snippet and to the right, but for some reason when I use these attributes they are positioned relative to the entire page, not to the parent div. Does anyone have a suggestion as to what I should do differently?
const styles = {
module: {
marginTop: '30px',
padding: '20px',
},
snippet: {
backgroundColor: '#f2f2f2',
border: 'solid 1px #ccc',
borderRadius: '4px',
margin: '0 auto',
padding: '10px',
width: '100%',
position: 'absolute',
left: '50%',
},
snippetButtonHolder: {
width: '95%',
position: 'relative',
},
button: {
float: 'right',
marginTop: '5px',
position: 'absolute',
left: '94%',
},
};
export default class CodeSnippet extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div style={styles.module}>
<div style={styles.snippetButtonHolder}>
<div style={styles.snippet}>
<div
{'text will go here'}
</div>
{this.state.showButton ?
<button
style={styles.button}>
Press me
</button>
: null}
</div>
</div>
</div>
);
}
}
Give this a try:
const styles = {
module: {
marginTop: '30px',
padding: '20px',
},
snippet: {
backgroundColor: '#f2f2f2',
border: 'solid 1px #ccc',
borderRadius: '4px',
display: 'inline-block',
overflow: 'hidden',
padding: '10px',
},
snippetButtonHolder: {
textAlign: 'center',
width: '95%',
},
button: {
float: 'right',
marginTop: '5px',
},
};

Categories