I am using modal with framer motion, the initial animation for the modal works fine, but on the exit it does not work at all. The modal disappears immediately for some reason.
This is where I am opening the modal:
const Agenda = () => {
const [modalOpen, setModalOpen] = useState(false);
const close = () => setModalOpen(false);
const open = () => setModalOpen(true);
return (
<>
<AnimatePresence
initial={false}
exitBeforeEnter={true}
>
{modalOpen && (
<Modal modalOpen={modalOpen} handleClose={close}>
<AgendaContent />
</Modal>
)}
</AnimatePresence>
</>
);
};
export default Agenda;
And here is the modal itself:
const newspaper = {
hidden: {
transform: "scale(0) rotate(720deg)",
opacity: 0,
transition: {
delay: 0.3,
},
},
visible: {
transform: " scale(1) rotate(0deg)",
opacity: 1,
transition: {
duration: 0.5,
},
},
exit: {
transform: "scale(0) rotate(-720deg)",
opacity: 0,
transition: {
duration: 0.3,
},
},
};
const Modal = ({ handleClose, children }) => {
return (
<Backdrop onClick={handleClose}>
<motion.div
drag
onClick={(e) => e.stopPropagation()}
className="modal"
variants={newspaper}
initial="hidden"
animate="visible"
exit="exit"
>
<img
className="close-icon"
src={CloseIcon}
alt="close"
onClick={handleClose}
/>
{children}
</motion.div>
</Backdrop>
);
};
export default Modal;
I followed this tutorial. I am not sure what I am doing wrong here, I am using the AnimatePresence there, any idea what could be wrong?
From the AnimatePresence docs:
Note: Direct children must each have a unique key prop so
AnimatePresence can track their presence in the tree.
It's frustratingly easy to forget even if you know about the requirement.
Something like this should work:
<AnimatePresence
initial={false}
exitBeforeEnter={true}
>
{modalOpen && (
<Modal key="modal" modalOpen={modalOpen} handleClose={close}>
<AgendaContent />
</Modal>
)}
</AnimatePresence>
Related
**I wonder how can I edit parent component styles (backgound-color for example) when I'm hovering the child's component element - add to Cart button. Now the styles are applyed to all of the parent cards components at the same time, but I need it to be applyed only to the item's parent component. **
------------------
Parent component
------------------
const Products = ({ products }) => {
const [hovered, setHovered] = useState(false);
return (
<>
<Product products={products} hovered={hovered} setHovered={setHovered} />
</>
);
};
------------------
Child component
------------------
const Product = ({products, hovered, setHovered, style }) => {
if (hovered) {
style = {
backgroundColor: "#ffcbcb",
color: "#fff",
transition: "all 0.5s ease"
};
} else {
style = {
backgroundColor: "#ececec",
color: "#000",
transition: "all 0.5s ease"
};
}
return (
<div className={styles.items}>
{products.map((value) => {
return (
<>
<div className={styles.item} key={id} style={style}>
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className={styles.btn}
>
Add to Cart
</button>
</div>
</>
);
})}
</div>
);
};
I modified the code to generate a toast stack into a ToastContainer dynamically as a notification system. Now the toast do not autoclose even in close button click.
The code can be reproduced by copy and past inside of dimiss area in https://react-bootstrap.github.io/components/toasts/
const Tsty = (props) =>
<>
<Toast show={ props.show } delay={ 3000 } autohide>
<Toast.Header>
<img src="holder.js/20x20?text=%20" className="rounded me-2" alt="" />
<strong className="me-auto">{props.title}</strong>
<small className="text-muted">{props.time}</small>
</Toast.Header>
<Toast.Body>{props.body}</Toast.Body>
</Toast>
</>
render(<Tsty />);
function Example() {
const [listNtfy, setNtfy] = useState([]);
const style = { position: 'fixed', bottom: '60px', right: '10px', zIndex: '1000'}
return (
<>
<Button onClick={() => {setNtfy([...listNtfy, {title: 'title #', time: 'now', body: new Date().toString(), show : true }])}}>Add Ntfy</Button>
<ToastContainer style={style}>
{ listNtfy.map( (x,i) => <Tsty show={ x.show } title={x.title + i} time={x.time} body={x.body} key={i} /> ) }
</ToastContainer>
</>
);
}
render(<Example />);
where is the error?
This doesn't close cause Tosty hook to show property that is not present in the hook. To solve it, you can define the show property and create a function to hide it. When all ntfys are closed, the list is cleaned.
const Tsty = (props) =>
<>
<Toast show={ props.show } onClose={ () => {props.fcn(props.idx) } } delay={ 5000 } autohide>
<Toast.Header>
<img src="holder.js/20x20?text=Q" className="rounded me-2" alt="" />
<strong className="me-auto">{props.title}</strong>
<small className="text-muted">{props.time}</small>
</Toast.Header>
<Toast.Body>{props.body}</Toast.Body>
</Toast>
</>
render(<Tsty />);
function Example() {
const [listNtfy, setNtfy] = useState([]);
// Function to autohide the ntfys
const autoHide = (key) => {
// Set visibility of Ntfy to hidden
listNtfy.filter((x,i) => i === key)[0].show = false;
// Apply the change
setNtfy([...listNtfy]);
// Check all notifications is hide (if yes, reset the list)
if (listNtfy.every(v => v.show === false))
setNtfy([]);
};
const style = { position: 'fixed', bottom: '60px', right: '10px', zIndex: '1000'}
return (
<>
<Button onClick={() => {setNtfy([...listNtfy, { title: 'title #' + listNtfy.length, time: 'now', body: new Date().toString(), show : true }])}}>Add Ntfy</Button>
<ToastContainer style={style}>
{ listNtfy.map( (x,i) => <Tsty show={ x.show } title={x.title} time={x.time} body={ x.body } key={i} idx={i} fcn={ autoHide } /> )}
</ToastContainer>
</>
);
}
render(<Example />);
I've been having an issue which returns the classic Hook issue:
×
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
Now it appears iv'e narrowed it done and it's my div, which is a div with animated css. Please see the code below for the styled component:
const BackDrop = styled(motion.div)`
position: absolute;
width: 160%;
height: 550px;
border-radius: 50%;
transform: rotate(60deg);
top: -290px;
left: -70px;
background: #8360c3; /* fallback for old browsers */
background: -webkit-linear-gradient(
to right,
#2ebf91,
#8360c3
); /* Chrome 10-25, Safari 5.1-6 */
`;
And it's inside of a function which I call to the App.js:
export function AccountBox(props) {
const { initialActive } = props;
const [isExpanded, setExpanded] = useState(false);
const [active, setActive] = useState(
initialActive ? initialActive : "signin"
);
const playExpandingEffect = () => {
setTimeout(() => {
setExpanded(false);
}, expandingTransition.duration * 1000 - 1500);
};
const switchActive = (active) => {
setTimeout(() => setActive(active), 400);
};
const switchToSignup = () => {
playExpandingEffect();
switchActive("signup");
};
const switchToSignin = () => {
playExpandingEffect();
switchActive("signin");
};
const contextValue = {
switchToSignup,
switchToSignin,
playExpandingEffect,
};
return (
<AccountContext.Provider value={contextValue}>
<BoxContainer>
<TopContainer>
<BackDrop
variants={backdropVariants}
transition={expandingTransition}
initial={false}
animate={isExpanded ? "expanded" : "collapsed"}
/>
{active === "signin" && (
<>
<HeaderContainer>
<HeaderText>Welcome</HeaderText>
<HeaderText>Back</HeaderText>
</HeaderContainer>
<SmallText>Please sign-in to continue!</SmallText>
</>
)}
{active === "signup" && (
<>
<HeaderContainer>
<HeaderText>Create </HeaderText>
<HeaderText>Account</HeaderText>
</HeaderContainer>
<SmallText>Please sign-up to continue!</SmallText>
</>
)}
</TopContainer>
<InnerContainer>
{active === "signin" && <LoginForm />}
{active === "signup" && <SignupForm />}
</InnerContainer>
</BoxContainer>
</AccountContext.Provider>
);
}
Now is the issue that i'm calling the Hook incorrectly? I can see some documentation online however I can't figure out why this is an issue as it's called in the function.
Does anybody knows!
I am creating a simple carousel with react and I noticed that my index gets call multiple times and I don't understand why, here is my snippet of code here also here is a full version https://codesandbox.io/s/small-bash-4l7ix?file=/src/index.js
...
const pages = [
React.forwardRef((props, ref) => (
<animated.div ref={ref} style={{ ...props.style, background: 'lightpink' }}>
A
</animated.div>
)),
React.forwardRef((props, ref) => (
<animated.div ref={ref} style={{ ...props.style, background: 'lightblue' }}>
B
</animated.div>
)),
React.forwardRef((props, ref) => (
<animated.div ref={ref} style={{ ...props.style, background: 'lightgreen' }}>
C
</animated.div>
)),
]
export default function App() {
const [index, set] = useState(0)
const [containerRef, containerSize] = useDimensions()
const transitions = useTransition(index, p => p, {
from: { opacity: 0, transform: 'translate3d(100%,0,0)' },
enter: { opacity: 1, transform: 'translate3d(0%,0,0)' },
leave: { opacity: 0, transform: 'translate3d(-50%,0,0)' },
})
const divStyle = {
height: `${containerSize.height}px`,
}
console.log(index)
return (
<>
<button className={`btn ${index === 0 ? 'btn--active' : ''}`} onClick={() => set(0)}>
Slide 1
</button>
<button className={`btn ${index === 1 ? 'btn--active' : ''}`} onClick={() => set(1)}>
Slide 2
</button>
<button className={`btn ${index === 2 ? 'btn--active' : ''}`} onClick={() => set(2)}>
Slide 3
</button>
<div style={divStyle} className="simple-trans-main">
{transitions.map(({ item, props, key }) => {
const Page = pages[item]
return <Page ref={containerRef} key={key} style={props} />
})}
</div>
<p> Lorem ipusum</p>
</>
)
}
...
In order to transition components in and out the useTransition hook from react-spring keeps track of component instances for you. These extra renders are caused by overlapping mounting and unmounting of nodes.
For example:
Start at 'Slide 1'
Click 'Slide 2'
Library mounts 'Slide 2' // Triggers rerender
Library starts to transition 'Slide 1' out
Library unmounts 'Slide 1' // Triggers rerender
Each transition is pushed into the array and the library animates them in order. So you can trigger multiple overlapping animations simultaneously.
Checkout the React DevTools in the codesandbox and you will see the nodes mounting and unmounting.
This is because of useTransition.
That creates an extra render of sorts, that is why you see index printed multiple times.
I have removed the useTransition and you can check it prints only on index change.
Check below.
I have a modal window with a keyboard in it. Everything's fine, except that I can't remove the scrollbar. I tried adding overflow:'hidden' as inline css but still nothing.
Also, even when using container-full padding-0 in bootstrap, the components still won't go till the edge of the screen. So I guess here's the problem.
This is where I render my component
<div className="container-full padding-0">
<div className="row">
<div className="col-sm-3">
<ButtonsGrid list={this.state.list} clicked={this.clicked}/>
</div>
<div className="col-sm-3" style={{paddingLeft:0, paddingRight:0}}>
<ButtonsGrid list = {this.state.list} clicked={this.clicked}/>
</div>
<div className="col-sm-6" style={{paddingRight: 0, paddingLeft: 0}}>
<Keyboard search={this.search}/> <-------------- HERE
</div>
</div>
</div>
And the component's render looks like this:
render() {
return(
<div>
<Paper
onClick={this.toggleKeyboard}>
<p
style={{
fontSize:40,
overflow:'hidden'}}>
{this.state.input !== '' ?
this.state.input : 'Search...'}
</p>
</Paper>
<br />
{this.state.showKeyboard ?
<Dialog
open={this.state.showKeyboard}
maxWidth='md'fullWidth>
<GridList
cellHeight={50}
cols={11}
style={{overflowX:'hidden'}}>
{this.state.keyboard.length > 0 ?
this.state.keyboard.map(key => {
return(
<Button
disabled={key.value === ''}
key={Math.random()*13}
style={{minWidth: '30px', border: '1px solid'}}
color="default"
onClick={key.value !== 'Enter' ?
() => this.onInputChanged(key.value) :
() => this.search(key.value)}>
<div
style={{fontSize:'15px',
display: 'flex',
justifyContent: 'center',
textAlign:'center'}}
>
{key.value}
</div>
</Button>
)
}):null}
</GridList>
</Dialog>:''}
</div>
);
}
Also, here's a visual.
If I inspect the element in the browser, I can just uncheck overflow and it removes it.
I tried adding overflow:'hidden' to the div where the component gets rendered but it still wouldn't work.
Any ideas?
Just set overflow on DialogContent:
<Dialog
fullWidth={true}
maxWidth="xl"
open={this.state.isChartOpen}
onClose={() => this.setState({ isChartOpen: false })}
>
<DialogContent style={{ overflow: "hidden" }}>
<ContractPriceChart contracts={this.props.contracts} />
</DialogContent>
</Dialog>
Inside your sx property add:
'&::-webkit-scrollbar': {display: none}
I solved this problem in functional component in following code.
You should manipulate the overflow attribute of "< html >" tag.
When isOpen is true it will add "overflow-hidden" class to the html tag.
And when isOpen is false, it will remove the "overflow-hidden" class from the html tag.
import React, { useEffect } from 'react';
import Dialog from '#material-ui/core/Dialog';
import DialogContent from '#material-ui/core/DialogContent';
const MyDialog = (props) => {
const { isOpen } = props;
useEffect(() => {
const htmlElement = document.querySelector('html');
if (isOpen && !htmlElement.classList.contains('overflow-hidden')) {
htmlElement.classList.add('overflow-hidden');
} else {
htmlElement.classList.remove('overflow-hidden');
}
}, []);
useEffect(() => {
const htmlElement = document.querySelector('html');
if (isOpen && !htmlElement.classList.contains('overflow-hidden')) {
htmlElement.classList.add('overflow-hidden');
} else {
htmlElement.classList.remove('overflow-hidden');
}
}, [isOpen]);
return (
<div>
<Dialog
open={isOpen}
maxWidth="xl"
>
<DialogContent>
Content 1
Content 2
</DialogContent>
</Dialog>
</div>
);
};
And add this class to your global style.
.overflow-hidden {
overflow: hidden !important;
}
have you tried adding !important? like this: overflow:'hidden !important'
put all dialog element in <Dialog><DialogContent>.....</DialogContent></Dialog>
put all dialog element in <Dialog><DialogContent>.....</DialogContent></Dialog>here code:
render() {
return(
<div>
<Paper
onClick={this.toggleKeyboard}>
<p
style={{
fontSize:40,
overflow:'hidden'}}>
{this.state.input !== '' ?
this.state.input : 'Search...'}
</p>
</Paper>
<br />
{this.state.showKeyboard ?
<Dialog
open={this.state.showKeyboard}
maxWidth='md'fullWidth>
<GridList
cellHeight={50}
cols={11}
style={{overflowX:'hidden'}}>
<DialogContent>
{this.state.keyboard.length > 0 ?
this.state.keyboard.map(key => {
return(
<Button
disabled={key.value === ''}
key={Math.random()*13}
style={{minWidth: '30px', border: '1px solid'}}
color="default"
onClick={key.value !== 'Enter' ?
() => this.onInputChanged(key.value) :
() => this.search(key.value)}>
<div
style={{fontSize:'15px',
display: 'flex',
justifyContent: 'center',
textAlign:'center'}}
>
{key.value}
</div>
</Button>
)
}):null}
</GridList>
</DialogContent>
</Dialog>:''}
</div>
);
}
Try utilizing the pseudo element -webkit-scrollbar to remove it:
.MuiDialog-paper::-webkit-scrollbar {
display: none;
}
if it doesn't work you can try:
.MuiDialog-root::-webkit-scrollbar {
display: none;
}
The downside is that you can't use it inline, but I tested here it works.
you can try with this:
<DialogContent className={classes.hiddenScroll}>
and their styles:
const useStyles = makeStyles(theme => ({
hiddenScroll: {
overflow: 'hidden',
},
I was using Material-ui Backdrop faced the same issue. Tried Fatih Turgut approach with a slight difference
import React, { useEffect, useState } from 'react';
import { Backdrop } from '#material-ui/core';
import { makeStyles } from '#material-ui/core';
const useStyles = makeStyles({
paper: {
zIndex: 20,
},
});
function Example() {
const [open, setOpen] = useState(true);
useEffect(() => {
const htmlElement = document.querySelector('body');
if (open || !htmlElement.classList.contains('overflow-hidden')) {
htmlElement.classList.add('overflow-hidden');
} else {
htmlElement.classList.remove('overflow-hidden');
}
}, [open]);
const classes = useStyles();
const handleOpen = open => {
setOpen(open);
};
return (
<Backdrop
className={classes.paper}
open={open}
onClick={() => handleOpen(!open)}
>
<h1>hello</h1>
</Backdrop>
);
}
export default Example;