简体   繁体   中英

React Timer using functional Hooks | Timer is not stopping

I want to create a time of 60s on react using hooks useState and useEffects This is what i am doing

import '../assets/css/timer.css'
import { useState, useEffect } from 'react'

const Timer = () =>{

    const [ time, setTime ] = useState(0);

    useEffect(()=>{
        if(time!==60){
            setInterval(()=>{
                    setTime(prevTime => prevTime+1) ;
            }, 1000);
        }
        
    
    }, [])


    return(
        <>

            <div className="circular">
                <div className="inner"></div>
                <div className="outer"></div>
                <div className="numb">
                    {time}  // Place where i am displaying time
                </div>
                <div className="circle">
                    <div className="dot">
                    <span></span>
                    </div>
                    <div className="bar left">
                    <div className="progress"></div>
                    </div>
                    <div className="bar right">
                    <div className="progress"></div>
                    </div>
                </div>
            </div>
        </>
    )
}

export default Timer

Timer is not stopping. It continues to go on for ever. I have tried this too

    useEffect(()=>{

        setInterval(()=>{
            if(time!==60)
                setTime(prevTime => prevTime+1) ;
        }, 1000);
    
    }, [])

Can some please explain where things are going wrong.

useEffect(..., []) will only run once, so time inside of it will never update. So you need to check prevTime inside of the setTime function, and then only increment if it's not 60. If it is , you should clear the interval, and then you should clear the interval in the cleanup of useEffect:

useEffect(()=>{
    const i = setInterval(() => {
        setTime(prevTime => {
            if (prevTime !== 60) return prevTime+1;
            clearInterval(i);
            return prevTime;
        });
    }, 1000);
    return () => clearInterval(i);
}, [])

You are close to having it working with your first attempt, but there are a few problems.

The main problem is that you pass an empty dependency array, meaning it will only run on the first render and not be updated on successive renders. Secondly you don't provide a return or 'clean up' meaning the interval is never cleared.

  useEffect(() => {
    if (time < 60) {
      const timer = setInterval(() => {
        setTime(prevTime => prevTime + 1);
      }, 1000);

      return () => clearInterval(timer);
    }
  }, [time])

Here we pass time in the dependency array and conditionally set the interval if time is less than your end time, 60 in your case but I shortened it to 5 so that you can see it stop. We also pass a return callback that will clear the interval at the end of each render cycle.

With this set up every time the setInterval updates the time state the useEffect will clear the interval at the end of the previous render, and then re-run in the current render setting the interval again if time is less than the limit.

The advantage of using the React render cycle this way, instead of clearing the interval in the interval callback, is that it gives you granular control of your timeout – allowing you to easily add further checks or extend/shorten the time based on other state values.

 const { useState, useEffect } = React; const Timer = () => { const [time, setTime] = useState(0); useEffect(() => { if (time < 5) { const timer = setInterval(() => { setTime(prevTime => prevTime + 1); }, 1000); return () => clearInterval(timer); } }, [time]) return ( <div className="circular"> <div className="numb"> {time} </div> </div> ) } ReactDOM.render( <Timer />, document.getElementById("root"));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div>

You have to clear setTimeout in Unmount state

for functional component

// Funtional component
useEffect(()=>{
  const i = setInterval(() => {
    setTime(prevTime => {
        if (prevTime !== 60) return prevTime+1;
        clearInterval(i);
        return prevTime;
    });
  }, 1000);
  return () => clearInterval(i);
}, [])

For class component

// Class component
componentWillUnmount() {
  this.clearInterval()
}

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