Reputation: 6326
I am working on a snackbar that appears/disappears after x
amount of time, and I would like the timer to be paused when I hover over the snackbar.
However, it seems that the way I have laid this out doesn't allow this to work, with the setTimeout
and the useState
...
I can't find much online about how this can be fixed that I can work out, so any help would be appreciated.
https://stackblitz.com/edit/react-dyq9mg
App.js
const App = () => {
const [showSnack, setShowSnack] = useState(false);
const toggle = () => {
setShowSnack(true);
setTimeout(() => {
setShowSnack(false);
}, 1000);
}
return (
<div>
<button onClick={toggle}>Toggle</button>
<Snack show={showSnack} />
</div>
);
}
render(<App />, document.getElementById('root'));
Snack.js
const Snack = ({show}) => {
return(
<div className={`my-snack ${show ? 'show' : ''}`}>My Snackbar</div>
);
}
index.css
.my-snack {
position: fixed;
width: 100%;
height: 500px;
background: lightgray;
bottom: 0;
visibility: hidden;
cursor: pointer;
}
.my-snack.show {
visibility: visible;
animation: fadein 0.5s, fadeout 0.5s 9.6s;
animation-play-state: running;
}
.my-snack:hover {
animation-play-state: paused paused;
}
/*Animations to fade the snackbar in and out*/
@keyframes fadein {
from {bottom: -500px;}
to {bottom: 0;}
}
@keyframes fadeout {
from {bottom: 0;}
to {bottom: -500px; opacity: 0;}
}
Upvotes: 0
Views: 427
Reputation: 6512
One way you could solve this is by using the onAnimationEnd event instead of a timer. I'd recommend this approach over a timer since you'd control all the timing through css and the component would react to when the animation has finished.
<Snack
show={showSnack}
onAnimationEnd={(e) => {
setShowSnack(false)
}}
/>
const Snack = ({ show, onAnimationEnd }) => {
return (
<div
className={`my-snack ${show ? 'show' : ''}`}
onAnimationEnd={(e) => {
if (e.animationName === 'fadeout') {
onAnimationEnd(e)
}
}}>
My Snackbar
</div>
)
}
One note about this specific approach (for your use case) is you have to know the animation name in the js since you're using multiple animations at once. e.g. (e.animationName === 'fadeout')
Example: https://stackblitz.com/edit/react-szkq7w
Upvotes: 1
Reputation: 1899
I believe you might be conflating the CSS animation to fade in the snackbar with the showing and hiding of the snackbar itself. You could use onMouseOver
and onMouseOut
to pause and start the timer respectively. Of course, you'd have to pass these to the Snack
component and set them on the DOM node.
const timer = useRef(null);
const clearSnackTimer = () => clearTimeout(timer.current);
const startSnackTimer = () => timer.current = setTimeout(() => setShowSnack(false), 1000);
const toggle = () => {
setShowSnack(!showSnack);
startSnackTimer();
};
return (
<div>
<button onClick={toggle}>Toggle</button>
<Snack show={showSnack} onMouseOver={clearSnackTimer} onMouseOut={startSnackTimer} />
</div>
);
Something is still wonky with the fadeout
transition, but I think this solves for the requirement to pause hiding the snackbar hiding on hover, if that is your intention.
Upvotes: 1