I have built this custom modal component that does an animation on entering and leaving. However i’ve run into a problem where sometimes, more oftan than not – when opening the modal the animation seems to stutter or jitter a bit. But the closing animation is always flawless.
What is also strange is, if I comment out setModalAnimation("ModalEntryAnim")
part in the useEffect, the entry animation still plays, but the exit animation does not(?)
Greatful for any ideas behind what could cause the stutter/jitteryness and/or being able to explain why commenting out that line of code causes the entry animation to still play. Thanks!
import { useClickOutside } from "hooks/useClickOutside"
import { ModalContentContainer, ModalText, ModalTitle, StyledCloseButton, StyledModal, StyledModalOverlay } from "./Modal.styles"
import { ReactNode, useEffect, useRef, useState } from "react"
import "./Modal.css"
type Props = {
readonly state: "open" | "closed"
readonly children: ReactNode
readonly onClose: () => void
}
export const Modal = ({state, children, onClose}: Props) => {
const modalRef = useRef<HTMLDivElement>(null)
const [closed, setClosed] = useState<boolean>(true)
const [modalAnimation, setModalAnimation] = useState<string>("")
const [overlayAnimation, setOverlayAnimation] = useState<string>("")
useClickOutside(modalRef, () => {
if (state === "open" && !closed) {
onClose()
}
})
useEffect(() => {
let timer: NodeJS.Timeout
if (state === "open") {
setModalAnimation("ModalEntryAnim")
setOverlayAnimation("OverlayEntryAnim")
setClosed(false)
} else if (state === "closed") {
setModalAnimation("ModalExitAnim")
setOverlayAnimation("OverlayExitAnim")
timer = setTimeout(() => {
setClosed(true)
}, 250)
}
return () => clearTimeout(timer)
}, [state])
if (state === "closed" && closed) return null
return (
<StyledModalOverlay className={overlayAnimation}>
<StyledModal className={modalAnimation} ref={modalRef}>
<StyledCloseButton onClick={onClose}/>
{children}
</StyledModal>
</StyledModalOverlay>
)
}
animations
@keyframes ModalEntryAninmation {
from {
transform: translateY(2rem);
}
to {
transform: translateY(0rem);
}
}
@keyframes ModalExitAninmation {
from {
transform: translateY(0rem);
}
to {
transform: translateY(-4rem);
}
}
@keyframes ModalOverlayEntryAninmation {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes ModalOverlayExitAninmation {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.ModalEntryAnim {
animation: ModalEntryAninmation 250ms forwards;
}
.ModalExitAnim {
animation: ModalExitAninmation 250ms forwards;
}
.OverlayEntryAnim {
animation: ModalOverlayEntryAninmation 250ms forwards;
}
.OverlayExitAnim {
animation: ModalOverlayExitAninmation 250ms forwards;
}