I am trying to build a modal which, when opened, only displays for ten seconds and then stops showing after that time.
I have tried to code this mechanism using setInterval and useState.
At the moment, this only works the first time when I click "Open Modal".
Each subsequent time I click "Open Modal", the timer messes up and counts down from less and less time and only displays for 5...then 2.. then 1 second.
After googling, everyone has suggested adding "ClearInterval" to the useEffect hook but that has not worked for me.
import gsap from 'gsap'
import Image from 'next/future/image'
import { type FC, useEffect, useState } from 'react'
import { useRef } from 'react'
import { Transition } from 'react-transition-group'
import logo from '@/assets/brand/logo.svg'
import styles from './Modal.module.scss'
const Modal: FC<any> = ({ show, setShow }) => {
const container = useRef<HTMLDivElement>(null)
const q = gsap.utils.selector(container)
const [timeLeft, setTimeLeft] = useState(10)
useEffect(() => {
const intervalId = setInterval(() => {
setTimeLeft((prev) => prev - 1)
}, 1000)
if (!show) return
() => {
clearInterval(intervalId)
}
}, [show])
useEffect(() => {
if (timeLeft > 0) return
setShow(false)
setTimeLeft(10)
}, [setShow, timeLeft])
const onEnter = () => {
gsap.set(container.current, { opacity: 0 })
gsap.set(q('*'), { opacity: 0, y: 8 })
}
const onEntering = () => {
gsap.timeline().to(container.current, { opacity: 1, duration: 0.3 }).to(q('*'), { opacity: 1, y: 0, stagger: 0.08 })
}
const onExiting = () => {
gsap.to(container.current, { autoAlpha: 0, duration: 0.3 })
}
return (
<Transition
in={show}
timeout={600}
mountOnEnter={true}
unmountOnExit={true}
nodeRef={container}
onEnter={onEnter}
onEntering={onEntering}
onExiting={onExiting}>
<div ref={container} className={styles.container}>
<Image src={logo} alt="Loopspeed" width={214} height={44} style={{ objectFit: 'contain' }} />
<p>Blah Blah </p>
This modal will close in <strong>{timeLeft}</strong> seconds.
</p>
</div>
</Transition>
)
}
export default Modal
Please let me know if I need to post any more code.
I have tried using setInterval and useState, and also setInterval, clearInterval and useState.
CodePudding user response:
Just a misc error with useEffect - if you setInterval in the useEffect - ensure
you are clearing it, for useEffect hook the "cleanup" function needs to be returned - return () => ....
useEffect(() => {
if (!show) return;
const intervalId = setInterval(() => {
setTimeLeft((prev) => prev - 1)
}, 1000)
return () => {
clearInterval(intervalId)
}
}, [show]);