简体   繁体   中英

SetInterval "Stacking" on top of each other

I'm working on this project which uses setInterval and I'm having a little trouble understanding how setInterval works. The problem I'm having is that the each time I call setInterval the function seems to stack on top of each other resulting in the object appearing multiple times rapidly. I'm not sure what I'm doing wrong in this case. Can someone please help me out thanks.

const [hit, setHit] = useState(0)
const [count, setCount] = useState(0)
var timer = null
var funcCalls = 0
const hitSound = new Audio("/hit.wav")
useEffect(()=>{
    timer = setInterval(moveSquare, 2000)
},[])
function handleHit(){
    hitSound.play()
    setHit(hit+1)
    resetSquare()
}
function resetSquare(){
    clearInterval(timer)
    moveSquare()
    timer = setInterval(moveSquare, 2000)
}
function moveSquare(){
    const gameContainer = document.getElementById("game-container")
    const height = gameContainer.offsetHeight
    const width = gameContainer.offsetWidth
    const square = document.getElementById("square")
    square.style.top = (Math.random() * (height - 100)) + "px";
    square.style.left = (Math.random() * (width - 100)) + "px";
}
return (
    <>
    <section className='game-section'>
        <div className='counter'>
            {hit}
            {count}
        </div>
        <div className='game-container' id = "game-container">
            <div className="square" id = "square" onClick={handleHit}></div>
        </div>
    </section>
    </>
)

It's because the timer value isn't saved across renders so you can't cancel it.

var timer = null // <-- no
const [timer, setTimer] = useState(null) // <-- yes
...
// later in useEffect() and resetSquare()
// save the timer using setTimer(timer)

Reason for that is elsewhere: your component (and/or and of its parent) is recreated. There are just few reasons in general but it's hard to point a specific line of code without debugging:

  • Either conditional rendering (eg showing spinner during request and after render element with children again)
  • Or different key prop is provided
  • Or by mistake you put component declaration(function or class or wrap into HOC) inside of other component's render code and every time parent is rerendered, component's declaration becomes referentially different

Another root cause: your useEffect does not have cleanup. While it should. And even without first reason, you would not have multiple concurrent timers but you still would have timer that would not stop when you're navigating away.

useEffect(()=>{
    timer = setInterval(moveSquare, 2000)
    return () => clearInterval(timer);
},[])

More on cleanup in official docs:https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM