React – Jittery animation on modal entry

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;
}