Lucas
Lucas

Reputation: 391

clearInterval in React

I'm new at React and I was trying to create a simple stopwatch with a start and stop buttons. I'm banging my head against the wall to try to clearInterval with an onClick event on Stop button. I would declare a variable for the setInterval and then would clear it using the clearInterval. Unfortunately it is not working. Any tips? Thank you in advance.

import React, { Component } from 'react';

class App extends Component {
  constructor(props){
    super(props);
    this.state = {time:0}

    this.startHandler = this.startHandler.bind(this);
  }

  getSeconds(time){
    return `0${time%60}`.slice(-2);
  }

  getMinutes(time){
    return Math.floor(time/60);
  }

  startHandler() {
      setInterval(()=>{
      this.setState({time:this.state.time + 1});
    },1000)

  }

  stopHandler() {
    //HOW TO CLEAR INTERVAL HERE????
  }

  render () {
    return (
      <div>
        <h1>{this.getMinutes(this.state.time)}:{this.getSeconds(this.state.time)}</h1>
        <button onClick = {this.startHandler}>START</button>
        <button onClick = {this.stopHandler}>STOP</button>
        <button>RESET</button>
      </div>
    );
  }
}

export default App;

Upvotes: 39

Views: 87195

Answers (8)

Mohamed Swadique K
Mohamed Swadique K

Reputation: 331

You can use setInterval inside useEffect with no dependency so it calls once when the component is initiated, then call the clearInterval when the component is unmounted.

useEffect(() => {
    let intervalId = setInterval(executingFunction,1000)
    return(() => {
        clearInterval(intervalId)
    })
},[])

Upvotes: 26

first
first

Reputation: 743

For those using functional react components, you can also use useIntervalEffect custom hook from react-hookz library. Read more about this and other hooks from their official documentation.

Upvotes: 0

Henry
Henry

Reputation: 15732

For React 16.8+ with hooks you can store the intervalID in a ref value (rather than in state) since the component does not need to rerender when the intervalID updates (and to always have access to the most recent intervalID).

Here's an example:

function Timer() {
    const [time, setTime] = React.useState(0);
    const intervalIDRef = React.useRef(null);

    const startTimer = React.useCallback(() => {
        intervalIDRef.current = setInterval(() => {
            setTime(prev => prev + 1);
        }, 1000);
    }, []);

    const stopTimer = React.useCallback(() => {
        clearInterval(intervalIDRef.current);
        intervalIDRef.current = null;
    }, []);

    // resetTimer works similarly to stopTimer but also calls `setTime(0)`

    React.useEffect(() => {
        return () => clearInterval(intervalIDRef.current); // to clean up on unmount
    }, []);

    return (
        <div>
            <span>Time: {time}</span>
            <button onClick={startTimer}>START</button>
            <button onClick={stopTimer}>STOP</button>
        </div>
    )
}

Note that for a timer component like this, it's a better idea to update the time by referencing the current time (with performance.now or new Date) relative to the last updated time than to increment a time variable since setInterval does not provide an accurate way of recording time and small inaccuracies will build up over time. You can check the timing with this script:

let lastTime = performance.now();
setInterval(() => {
    const currentTime = performance.now();
    console.log(currentTime - lastTime);
    lastTime = currentTime;
}, 1000);

Upvotes: 9

Dinesh Patil
Dinesh Patil

Reputation: 1072

you can add interval to your component's state and can clear it whenever you want.

componentDidMount(){
  const intervalId = setInterval(this.yourFunction, 1000)
  this.setState({ intervalId })
}

componentWillUnmount(){
  clearInterval(this.state.intervalId)
}

Upvotes: 51

HarshitRV
HarshitRV

Reputation: 13

componentWillUnmount() will do the trick for stopping as well as resetting the stopwatch. You can find more on this on react docs

import React, { Component } from 'react';

class StopWatch extends Component {
  constructor(props){
    super(props);
    this.state = {
        time : 0
    }

    this.startHandler = this.startHandler.bind(this);
    this.resetHandler = this.resetHandler.bind(this);
    this.componentWillUnmount = this.componentWillUnmount.bind(this);
  }

  // Start the stopwatch
  startHandler() {
    this.stopWatchID = setInterval(()=>{
      this.setState({time:this.state.time + 1});
    },1000);
  }

  // Stop the stopwatch
  componentWillUnmount() {
    clearInterval(this.stopWatchID);
  }

  // Reset the stopwatch
  resetHandler(){
    this.setState({
        time: 0
    })
    this.componentWillUnmount();
  }

  getSeconds(time){
    return `0${time%60}`.slice(-2);
  }

  getMinutes(time){
    return Math.floor(time/60);
  }

  render () {
    return (
      <div>
        <h1>{this.getMinutes(this.state.time)}:{this.getSeconds(this.state.time)}</h1>
        <button onClick = {this.startHandler}>START</button>
        <button onClick = {this.componentWillUnmount}>STOP</button>
        <button onClick = {this.resetHandler} >RESET</button>
      </div>
    );
  }
}

export default StopWatch;

Upvotes: 1

Sammy Aguns
Sammy Aguns

Reputation: 133

Create an ID for the timer, then Change your start startHandler and stopHandler as below;

  let this.intervalID;

  startHandler() {
      this.intervalID = setInterval(()=>{
      this.setState({time:this.state.time + 1});
    },1000)

  }

  stopHandler() {
    clearInterval(intervalID)
  }

Upvotes: -1

Daniel Andrei
Daniel Andrei

Reputation: 2684

In your startHandler function you can do :

    this.myInterval = setInterval(()=>{
      this.setState({ time: this.state.time + 1 });
    }, 1000);

and in your stopInterval() you would do clearInterval(this.myInterval);

Upvotes: 15

Murat Karag&#246;z
Murat Karag&#246;z

Reputation: 37624

You can use clearInterval(id) to stop it. You have to store the id of the setInterval e.g.

const id = setInterval(() = > {
    this.setState({
        time: this.state.time + 1
    });
}, 1000)
clearInterval(id);

Upvotes: 7

Related Questions