I have a ponent that has dynamic class names based on the state of the ponent. The initial state of the menu ponent is false
so an exit animation triggers when I load the site. I'm trying to not play the exit animation on initial render, I'm trying to trigger the animation only when the user clicks away from the menu.
@keyframes menuEnter {
from {
transform: translateY(-100vh);
}
to {
transform: translateY(0);
}
}
@keyframes menuExit {
from {
transform: translateY(0);
}
to {
transform: translateY(-100vh);
}
}
const [showMenu, setShowMenu] = useState(false)
<div className={`menuContainer ${showMenu ? `menuEnter` : 'menuExit'}`} id='menu'>
I'm trying to bail out of the exit animation because it doesn't make sense to play the exit animation on initial render.
The state changes onClick, when state is true
onClick sets it to false
and when state is false
the onClick sets it to true
.
I have a ponent that has dynamic class names based on the state of the ponent. The initial state of the menu ponent is false
so an exit animation triggers when I load the site. I'm trying to not play the exit animation on initial render, I'm trying to trigger the animation only when the user clicks away from the menu.
@keyframes menuEnter {
from {
transform: translateY(-100vh);
}
to {
transform: translateY(0);
}
}
@keyframes menuExit {
from {
transform: translateY(0);
}
to {
transform: translateY(-100vh);
}
}
const [showMenu, setShowMenu] = useState(false)
<div className={`menuContainer ${showMenu ? `menuEnter` : 'menuExit'}`} id='menu'>
I'm trying to bail out of the exit animation because it doesn't make sense to play the exit animation on initial render.
The state changes onClick, when state is true
onClick sets it to false
and when state is false
the onClick sets it to true
.
-
you need to rethink the conditions. You should add
menuExit
only if your menu is opened. And also add the classes on an event triggered by your menu trigger ( hover, click whatever ) – Mihai T Commented Aug 29, 2020 at 17:32 - Edited my question. What would be another way dealing with the conditions? – Jonatan Tibarovsky Commented Aug 29, 2020 at 17:40
3 Answers
Reset to default 5The javascript is okay, it's the CSS that needs fixing. Instead of using animations, just use transitions so the menu doesn't go from open to close on render.
Try the following:
Add a transition to your menu id:
#menu {
transition: 0.3s ease-out;
}
Then change this value by switching between a menu open and close class:
.menuEnter {
transform: translateY(0);
}
.menuExit {
transform: translateY(-100vh);
}
That way, when the ponent renders, the menu doesn't go from one animation state to another, it remains closed.
PS: Consider using 3d transforms for performance reasons.
You can achieve this by defaulting showMenu to undefined,
const [showMenu, setShowMenu] = useState(undefined)
<div className={`menuContainer ${showMenu ? `menuEnter` : showMenu === undefined ? '' : 'menuExit'}`} id='menu'>
I had same issue and I ended up adding transition class to onClick before menu gets opened or closed:
const { useState,useEffect } = React
const Example = () => {
const [showMenu, setShowMenu] = useState(false)
const [menuMounted, setMenuMounted] = useState(false)
const mountedClass = menuMounted ? 'mounted' : ''
const showMenuClass = showMenu ? 'menuOpen' : 'menuClosed'
useEffect(() => {
// imitate redux state change
setTimeout(() => setShowMenu(true), 2000)
}, [])
return (
<>
<button onClick={() => {
setMenuMounted(true)
setShowMenu(!showMenu)
}}>Toggle</button>
<div className={`menuContainer ${showMenuClass} ${mountedClass}`}>
1<br/>1<br/>1<br/>1<br/>1<br/>1<br/>
</div>
</>
)
}
ReactDOM.createRoot(document.getElementById("root")).render(
<Example />
)
.menuContainer {
overflow: hidden;
}
.mounted {
transition:
height 0.3s ease-in-out,
box-shadow 0.1s linear;
}
.menuClosed {
height: 0;
}
.menuOpen {
height: 110px;
}
<script src="https://cdnjs.cloudflare./ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>