setInterval in a React app

JavascriptReactjsSettimeoutState

Javascript Problem Overview


I'm still fairly new at React, but I've been grinding along slowly and I've encountered something I'm stuck on.

I am trying to build a "timer" component in React, and to be honest I don't know if I'm doing this right (or efficiently). In my code below, I set the state to return an object { currentCount: 10 } and have been toying with componentDidMount, componentWillUnmount, and render and I can only get the state to "count down" from 10 to 9.

Two-part question: What am I getting wrong? And, is there a more efficient way of going about using setTimeout (rather than using componentDidMount & componentWillUnmount)?

Thank you in advance.

import React from 'react';

var Clock = React.createClass({

  getInitialState: function() {
    return { currentCount: 10 };
  },

  componentDidMount: function() {
    this.countdown = setInterval(this.timer, 1000);
  },

  componentWillUnmount: function() {
    clearInterval(this.countdown);
  },

  timer: function() {
    this.setState({ currentCount: 10 });
  },

  render: function() {
    var displayCount = this.state.currentCount--;
    return (
      <section>
        {displayCount}
      </section>
    );
  }

});

module.exports = Clock;

Javascript Solutions


Solution 1 - Javascript

I see 4 issues with your code:

  • In your timer method you are always setting your current count to 10
  • You try to update the state in render method
  • You do not use setState method to actually change the state
  • You are not storing your intervalId in the state

Let's try to fix that:

componentDidMount: function() {
   var intervalId = setInterval(this.timer, 1000);
   // store intervalId in the state so it can be accessed later:
   this.setState({intervalId: intervalId});
},

componentWillUnmount: function() {
   // use intervalId from the state to clear the interval
   clearInterval(this.state.intervalId);
},

timer: function() {
   // setState method is used to update the state
   this.setState({ currentCount: this.state.currentCount -1 });
},

render: function() {
    // You do not need to decrease the value here
    return (
      <section>
       {this.state.currentCount}
      </section>
    );
}

This would result in a timer that decreases from 10 to -N. If you want timer that decreases to 0, you can use slightly modified version:

timer: function() {
   var newCount = this.state.currentCount - 1;
   if(newCount >= 0) { 
       this.setState({ currentCount: newCount });
   } else {
       clearInterval(this.state.intervalId);
   }
},

Solution 2 - Javascript

Updated 10-second countdown using class Clock extends Component

import React, { Component } from 'react';

class Clock extends Component {
  constructor(props){
	super(props);
	this.state = {currentCount: 10}
  }
  timer() {
	this.setState({
	  currentCount: this.state.currentCount - 1
	})
	if(this.state.currentCount < 1) { 
	  clearInterval(this.intervalId);
	}
  }
  componentDidMount() {
	this.intervalId = setInterval(this.timer.bind(this), 1000);
  }
  componentWillUnmount(){
	clearInterval(this.intervalId);
  }
  render() {
	return(
	  <div>{this.state.currentCount}</div>
	);
  }
}

module.exports = Clock;

Solution 3 - Javascript

Updated 10-second countdown using Hooks (a new feature proposal that lets you use state and other React features without writing a class. They’re currently in React v16.7.0-alpha).

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

const Clock = () => {
    const [currentCount, setCount] = useState(10);
    const timer = () => setCount(currentCount - 1);

    useEffect(
        () => {
            if (currentCount <= 0) {
                return;
            }
            const id = setInterval(timer, 1000);
            return () => clearInterval(id);
        },
        [currentCount]
    );

    return <div>{currentCount}</div>;
};

const App = () => <Clock />;

ReactDOM.render(<App />, document.getElementById('root'));

Solution 4 - Javascript

If anyone is looking for a React Hook approach to implementing setInterval. Dan Abramov talked about it on his blog. Check it out if you want a good read about the subject including a Class approach. Basically the code is a custom Hook that turns setInterval as declarative.

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

Also posting the CodeSandbox link for convenience: https://codesandbox.io/s/105x531vkq

Solution 5 - Javascript

Manage setInterval with React Hooks:

  const [seconds, setSeconds] = useState(0)

  const interval = useRef(null)

  useEffect(() => { if (seconds === 60) stopCounter() }, [seconds])

  const startCounter = () => interval.current = setInterval(() => {
    setSeconds(prevState => prevState + 1)
  }, 1000)

  const stopCounter = () => clearInterval(interval.current)

Solution 6 - Javascript

Thanks @dotnetom, @greg-herbowicz

If it returns "this.state is undefined" - bind timer function:

constructor(props){
    super(props);
    this.state = {currentCount: 10}
    this.timer = this.timer.bind(this)
}

Solution 7 - Javascript

The easy thing to do is to add it to the window variable.

useEffect(() => {
    window.interval23 = setInterval(
      () => setState('something'),
      2500
    )
    return () => {
      clearInterval(window.interval23)
    }
 }, [])

but make sure whatever you create with the window variable, keep it as unique as possible because the window variable might interrupt in libraries if that variable already exists.

Solution 8 - Javascript

Updating state every second in the react class. Note the my index.js passes a function that return current time.

import React from "react";

class App extends React.Component {
  constructor(props){
    super(props)
    
    this.state = {
      time: this.props.time,
      
    }        
  }
  updateMe() {
    setInterval(()=>{this.setState({time:this.state.time})},1000)        
  }
  render(){
  return (
    <div className="container">
      <h1>{this.state.time()}</h1>
      <button onClick={() => this.updateMe()}>Get Time</button>
    </div>
  );
}
}
export default App;

Solution 9 - Javascript

If you are using Dan Abramov useInterval hook and want to manually cancel the current interval you just need to call the hook again passing null as delay variable.

You can check a working example here https://codesandbox.io/s/useinterval-cancel-interval-dan-abramov-extended-oe45z?file=/src/index.js

Solution 10 - Javascript

You can use interval to set state by creating what I call a fake recursion by combining setTimeout and useEffect

  import {useEffect,useState} from 'react'

  const [state,setState]=useState(0)

  function Interval(){
      setTimeout(()=>{
         setState(state+1)
       },2000)
  }
   useEffect(()=>Interval(),[state])

 //this code runs repeatedly in interval of 2 seconds

Solution 11 - Javascript

import React, { useState, useEffect } from "react";

export const Count = () => {
const [currentCount, setCount] = useState(1);

const timer = () => setCount(currentCount + 1);

useEffect(
    () => {
        if (currentCount <= 0) {
            return;
        }
        const id = setInterval(timer, 1000);
        return () => clearInterval(id);
    },
    [currentCount]
);

console.log(currentCount)

return <div>Count : - {currentCount}</div>;
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJoseView Question on Stackoverflow
Solution 1 - JavascriptdotnetomView Answer on Stackoverflow
Solution 2 - JavascriptGreg HerbowiczView Answer on Stackoverflow
Solution 3 - JavascriptGreg HerbowiczView Answer on Stackoverflow
Solution 4 - JavascriptJo E.View Answer on Stackoverflow
Solution 5 - JavascriptGuilherme TrivilinView Answer on Stackoverflow
Solution 6 - JavascripttulsluperView Answer on Stackoverflow
Solution 7 - JavascriptRam KishoreView Answer on Stackoverflow
Solution 8 - JavascriptAshok ShahView Answer on Stackoverflow
Solution 9 - JavascriptJuanma MenendezView Answer on Stackoverflow
Solution 10 - JavascriptVictor KhachiView Answer on Stackoverflow
Solution 11 - JavascriptDeepak SinghView Answer on Stackoverflow