Home > Software engineering >  setState not setting some states
setState not setting some states

Time:07-25

Im creating a clock that has minutes and seconds. This is how my component looks like

class Time extends React.PureComponent
{

constructor(props)
    {
        super(props);
        this.state = 
        {
            hrs: 0,
            mins: 0,
            secs: 0
        }

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

tick()
    {
        //This increments the minutes
        if(this.state.secs  >= 59)
        {
            this.setState({mins: this.state.mins 1});
            this.setState({secs: 0});
        }
        //This increments the hours
        if(this.state.minutes >= 59)
        {
            this.setState({hrs: this.state.hrs 1});
            this.setState({mins: 0});
        }
        else
        {
            //This increments the seconds
            this.setState({secs: this.state.secs 1});
        }
    }

    componentDidMount()
    {
        this.tock = setInterval(()=>
        {
            this.tick();
        }, 1000)
    }

    componentWillUnmount()
    {
        clearInterval(this.tock);
    }

     render()
    {
        return(
            <div>
                <h1>Time</h1>
                <section>
                    {this.state.hrs}:{this.state.mins}:{this.state.secs}
                </section>
            </div>
        );
    }
}

}

export default Time;

The mins do increment to 59 and in turn increment the hrs - then reset to 0. But the secs just keep going and never reset to 0. They do update the mins though. The problem is that they go from 0 to infinity and dont stop counting. How can I make them reset to 0 each time?

CodePudding user response:

setState is asynchronous, so you cannot ensure all the state updates in the sequence with multiple setState. I'd suggest that you should have only one setState at the end of that function after all secs, mins, and hrs computed.

Side note I also fixed your wrong value in this.state.minutes to this.state.mins.

class Time extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      hrs: 0,
      mins: 0,
      secs: 0,
    };

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

  tick() {
    let { secs, mins, hrs } = this.state;
    
    secs  = 1;

    if (secs > 59) {
      mins  = 1;
      secs = 0;
    }

    if (mins > 59) {
      hrs  = 1;
      mins = 0;
    }

    this.setState({ secs, mins, hrs });
  }

  componentDidMount() {
    this.tock = setInterval(() => {
      this.tick();
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.tock);
  }

  render() {
    return (
      <div>
        <h1>Time</h1>
        <section>
          {this.state.hrs}:{this.state.mins}:{this.state.secs}
        </section>
      </div>
    );
  }
}

CodePudding user response:

You could also use a functional based component to achieve the same effect.

function Time() {
  const [time, setTime] = useState({
    secs: 50,
    mins: 58,
    hrs: 0
  });

  const tick = () => {
    setTime(prevState => ({...prevState, secs: prevState.secs   1}));
  }

  useEffect(() => {
    setInterval(() => {
      tick()
    }, 1000)
  }, []);

  useEffect(() => {
    if(time.secs > 59) {
      setTime(prevState => ({ ...prevState, secs: 0, mins: prevState.mins   1 }));
    }
  }, [time.secs])

  useEffect(() => {
    if (time.mins > 59) {
      setTime(prevState => ({ ...prevState, mins: 0, hrs: prevState.hrs   1 }));
    }
  }, [time.mins])

    return (
      <div>
        <h1>Time</h1>
        <section>
          {time.hrs}:{time.mins}:{time.secs}
        </section>
      </div>
    );
  
}

export default Time;
  • Related