admin管理员组

文章数量:1415684

Forgive me if this seems overly simple...this is the first thing I have ever made in React so I'm just trying to wrap my head around things. I'm already realizing I should have smaller ponent like buttons and render them with props and all that (goal is to refactor later!) but currently I am having a hard time figuring out how to use the setInterval method to change the state, and then stop that.

I'm building a pomodoro timer, and the general idea is that my state is maintaining the total seconds that the timer should have left. I have another function that will convert the total seconds into a time format I want to display.

My struggle is within my startStop() method, I would like the change the state of the running (the timer is running) to t/f and that will work, but I am clearly doing something wring with the setInterval. I want to set an interval (when there is time remaining) to change the state every second to be 1 second less. When I click the button again, the interval timer would stop and the current "state" of seconds remaining would be the same, so if you clicked the button again it would just start the timer again.

Thanks for the help! (This is all getting rendered from the create-react-app so there is more at my github is needed: )

    import React, { Component } from "react ";

    class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      sessionTimeEntry: 25, //in min
      breakTimeEntry: 5, //in min
      sessionRemainingSeconds: 1500, //in seconds
      breakRemainingSeconds: 300, //in seconds
      running: false,
      timerLabel: "Session"
    }
    this.addSession = this.addSession.bind(this);
    this.subSession = this.subSession.bind(this);
    this.addBreak = this.addBreak.bind(this);
    this.subBreak = this.subBreak.bind(this);
    this.startStop = this.startStop.bind(this);
    this.resetTimer = this.resetTimer.bind(this);
    this.formatMinutes = this.formatMinutes.bind(this);
  }

  addSession() { //adding and subtracting methods need to also chage the session remaining in seconds to mirrow the entry time if ever changed
    this.setState({
      sessionTimeEntry: this.state.sessionTimeEntry + 1,
      sessionRemainingSeconds: this.state.sessionRemainingSeconds + 60
    })
  }

  subSession() {
    this.setState({
      sessionTimeEntry: this.state.sessionTimeEntry - 1,
      sessionRemainingSeconds: this.state.sessionRemainingSeconds - 60

    })
  }

  addBreak() {
    this.setState({
      breakTimeEntry: this.state.breakTimeEntry + 1,
      breakRemainingSeconds: this.state.breakRemainingSeconds + 60
    })
  }

  subBreak() {
    this.setState({
      breakTimeEntry: this.state.breakTimeEntry - 1,
      breakRemainingSeconds: this.state.breakRemainingSeconds - 60
    })
  }

  startStop() {

    let timer;
    const status = this.state.running;

    switch (status) {
      case false:
        console.log("should start!")
        this.setState({
          running: true
        })

        while (this.state.breakRemainingSeconds > 0) {
          timer = setInterval(() => {
            this.setState({
              breakRemainingSeconds: this.state.breakRemainingSeconds - 1
            });
            console.log(this.state.breakRemainingSeconds);
          }, 1000)
        }

        break;
      case true:
        console.log("should stop")
        this.setState({
          running: false
        })
        clearInterval(timer)
        break;
      default:
        break;
    }

  }

  resetTimer() {
    this.setState({
      sessionTimeEntry: 25,
      breakTimeEntry: 5,
      sessionRemainingSeconds: 1500,
      breakRemainingSeconds: 300,
      running: false,
      timerLabel: "Session"
    })
  }

  formatMinutes(time) {
    let seconds = time;
    const minutes = (seconds % 60 === 0) ? ((seconds / 60) < 10 ? "0" + seconds / 60 : seconds / 60) : (Math.floor(seconds / 60) < 10 ? "0" + Math.floor(seconds / 60) : Math.floor(seconds / 60));
    seconds = (seconds % 60 === 0) ? "00" : ((seconds % 60 < 10) ? "0" + (seconds % 60) : seconds % 60)
    console.log(minutes + ":" + seconds);
    return minutes + ":" + seconds;
  }

  render() {
    return ( <
      div >
      <
      h1 > Pomodoro Clock < /h1> <
      h2 > {
        this.state.sessionTimeEntry
      } < /h2> <
      div id = 'timerContainer' >
      <
      h3 id = "session-label" > Session Time < /h3> <
      h3 id = "session-length" > {
        this.formatMinutes(this.state.sessionRemainingSeconds)
      } < /h3> <
      button onClick = {
        this.addSession
      }
      id = "session-increment" > ^ < /button> <
      button onClick = {
        this.subSession
      }
      id = "session-decrement" > v < /button> <
      /div> <
      div id = 'timerContainer' >
      <
      h3 id = "break-label" > Break Time < /h3> <
      h3 id = "break-length" > {
        this.state.breakTimeEntry
      } < /h3> <
      button onClick = {
        this.addBreak
      }
      id = "break-increment" > ^ < /button> <
      button onClick = {
        this.subBreak
      }
      id = "break-decrement" > v < /button> <
      /div> <
      div >
      <
      button onClick = {
        this.startStop
      }
      id = "start-stop" > Start / Stop < /button> <
      button onClick = {
        this.resetTimer
      }
      id = "reset" > Reset < /button> <
      /div> <
      /div>
    )
  }

}

export default App;

**************** UPDATE *****************

Figured it all out! Here is a link to the working codepen to see it in action.

Forgive me if this seems overly simple...this is the first thing I have ever made in React so I'm just trying to wrap my head around things. I'm already realizing I should have smaller ponent like buttons and render them with props and all that (goal is to refactor later!) but currently I am having a hard time figuring out how to use the setInterval method to change the state, and then stop that.

I'm building a pomodoro timer, and the general idea is that my state is maintaining the total seconds that the timer should have left. I have another function that will convert the total seconds into a time format I want to display.

My struggle is within my startStop() method, I would like the change the state of the running (the timer is running) to t/f and that will work, but I am clearly doing something wring with the setInterval. I want to set an interval (when there is time remaining) to change the state every second to be 1 second less. When I click the button again, the interval timer would stop and the current "state" of seconds remaining would be the same, so if you clicked the button again it would just start the timer again.

Thanks for the help! (This is all getting rendered from the create-react-app so there is more at my github is needed: https://github./ryanmdoyle/web-pomodoro)

    import React, { Component } from "react ";

    class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      sessionTimeEntry: 25, //in min
      breakTimeEntry: 5, //in min
      sessionRemainingSeconds: 1500, //in seconds
      breakRemainingSeconds: 300, //in seconds
      running: false,
      timerLabel: "Session"
    }
    this.addSession = this.addSession.bind(this);
    this.subSession = this.subSession.bind(this);
    this.addBreak = this.addBreak.bind(this);
    this.subBreak = this.subBreak.bind(this);
    this.startStop = this.startStop.bind(this);
    this.resetTimer = this.resetTimer.bind(this);
    this.formatMinutes = this.formatMinutes.bind(this);
  }

  addSession() { //adding and subtracting methods need to also chage the session remaining in seconds to mirrow the entry time if ever changed
    this.setState({
      sessionTimeEntry: this.state.sessionTimeEntry + 1,
      sessionRemainingSeconds: this.state.sessionRemainingSeconds + 60
    })
  }

  subSession() {
    this.setState({
      sessionTimeEntry: this.state.sessionTimeEntry - 1,
      sessionRemainingSeconds: this.state.sessionRemainingSeconds - 60

    })
  }

  addBreak() {
    this.setState({
      breakTimeEntry: this.state.breakTimeEntry + 1,
      breakRemainingSeconds: this.state.breakRemainingSeconds + 60
    })
  }

  subBreak() {
    this.setState({
      breakTimeEntry: this.state.breakTimeEntry - 1,
      breakRemainingSeconds: this.state.breakRemainingSeconds - 60
    })
  }

  startStop() {

    let timer;
    const status = this.state.running;

    switch (status) {
      case false:
        console.log("should start!")
        this.setState({
          running: true
        })

        while (this.state.breakRemainingSeconds > 0) {
          timer = setInterval(() => {
            this.setState({
              breakRemainingSeconds: this.state.breakRemainingSeconds - 1
            });
            console.log(this.state.breakRemainingSeconds);
          }, 1000)
        }

        break;
      case true:
        console.log("should stop")
        this.setState({
          running: false
        })
        clearInterval(timer)
        break;
      default:
        break;
    }

  }

  resetTimer() {
    this.setState({
      sessionTimeEntry: 25,
      breakTimeEntry: 5,
      sessionRemainingSeconds: 1500,
      breakRemainingSeconds: 300,
      running: false,
      timerLabel: "Session"
    })
  }

  formatMinutes(time) {
    let seconds = time;
    const minutes = (seconds % 60 === 0) ? ((seconds / 60) < 10 ? "0" + seconds / 60 : seconds / 60) : (Math.floor(seconds / 60) < 10 ? "0" + Math.floor(seconds / 60) : Math.floor(seconds / 60));
    seconds = (seconds % 60 === 0) ? "00" : ((seconds % 60 < 10) ? "0" + (seconds % 60) : seconds % 60)
    console.log(minutes + ":" + seconds);
    return minutes + ":" + seconds;
  }

  render() {
    return ( <
      div >
      <
      h1 > Pomodoro Clock < /h1> <
      h2 > {
        this.state.sessionTimeEntry
      } < /h2> <
      div id = 'timerContainer' >
      <
      h3 id = "session-label" > Session Time < /h3> <
      h3 id = "session-length" > {
        this.formatMinutes(this.state.sessionRemainingSeconds)
      } < /h3> <
      button onClick = {
        this.addSession
      }
      id = "session-increment" > ^ < /button> <
      button onClick = {
        this.subSession
      }
      id = "session-decrement" > v < /button> <
      /div> <
      div id = 'timerContainer' >
      <
      h3 id = "break-label" > Break Time < /h3> <
      h3 id = "break-length" > {
        this.state.breakTimeEntry
      } < /h3> <
      button onClick = {
        this.addBreak
      }
      id = "break-increment" > ^ < /button> <
      button onClick = {
        this.subBreak
      }
      id = "break-decrement" > v < /button> <
      /div> <
      div >
      <
      button onClick = {
        this.startStop
      }
      id = "start-stop" > Start / Stop < /button> <
      button onClick = {
        this.resetTimer
      }
      id = "reset" > Reset < /button> <
      /div> <
      /div>
    )
  }

}

export default App;

**************** UPDATE *****************

Figured it all out! Here is a link to the working codepen to see it in action.

https://codepen.io/ryanmdoyle/pen/vaxoaG

Share Improve this question edited Jul 24, 2018 at 20:12 Ryan asked Jul 22, 2018 at 20:46 RyanRyan 792 silver badges10 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 2

I think problem is in your startStop function. Please remove while loop from your function.

startStop() {
    const status = this.state.running;

    switch (status) {
        case false:
          console.log("should start!")
          this.setState({
              running: true
          })

          this.timer = setInterval(() => {
              this.setState({
                  breakRemainingSeconds: this.state.breakRemainingSeconds - 1
              });
              console.log(this.state.breakRemainingSeconds);
          }, 1000)
        }

        break;
        case true:
          console.log("should stop")
          this.setState({
            running: false
          }) 
          clearInterval(this.timer)
          break;
        default:
          break;
    }

}

You could create a setInterval that decreases breakRemainingSeconds with one every second, and store the id returned by setInterval on the instance. You can later use clearInterval(this.timer) when you want to stop the timer.

startStop() {
  const { running } = this.state;

  if (running) {
    this.setState({
      running: false
    });
    clearInterval(this.timer);
  } else {
    this.setState({
      running: true
    });

    this.timer = setInterval(() => {
      this.setState(previousState => {
        return {
          breakRemainingSeconds: previousState.breakRemainingSeconds - 1
        };
      });
    }, 1000);
  }
}

I suggest using a library like moment. Just install it as a dependency: npm i react-moment because JS can be a little bit tricky with dates and time. Anyways, here's a function that can get you started. I hope it helps. I used your breakRemainingSeconds for demonstration, but you can tweak it to achieve all the 'time' management you also may need in your ponent.

    class Timer extends React.Component {
    constructor() {
    super();
    this.state = { 
    time: {}, 
    breakRemainingSeconds: 300
    };
    
    this.timer = 0;
    this.startTimer = this.startTimer.bind(this);
    this.countDown = this.countDown.bind(this);
  }

// Let's make some sense of JS date and time It can get a little bit tricky sometimes.
// So, what we're doing here is taking the values and converting it in hours minutes, seconds. 
// In the example below we are using minutes and seconds, but just in case we got hours in there too :)

    createTime(secs){
      let hours = Math.floor(secs / (60 * 60));
      let divisor_for_minutes = secs % (60 * 60);
      let minutes = Math.floor(divisor_for_minutes / 60);
      let divisor_for_seconds = divisor_for_minutes % 60;
      let seconds = Math.ceil(divisor_for_seconds);

    let timeObject = {
      "h": hours,
      "m": minutes,
      "s": seconds
    };
    return timeObject;
  }

  ponentDidMount() {
 // Taking the starting point  -> breakRemainingSeconds <-
// Passing it as the parameter and setting the state's time object to it.
    let timeLeft = this.createTime(this.state.breakRemainingSeconds);
    this.setState({ time: timeLeft });
  }

// Check the current state and potentially (if != 0) start our main function 
  startTimer() {
    if (this.timer == 0) {
      this.timer = setInterval(this.countDown, 1000);
    }
  }

countDown() {
    // Remove one second, set state so a re-render happens.
    let seconds = this.state.breakRemainingSeconds - 1;
    this.setState({
      time: this.createTime(seconds),
      breakRemainingSeconds: seconds
    });
    
    // Check if we're at zero, and if so, clear the Interval
    if (seconds == 0) { 
      clearInterval(this.timer);
    }
  }

  render() {
    return(
      <div>
        <button 
                onClick={this.startTimer} style={{marginRight:'12px'}}>Let's Go</button>
        m: {this.state.time.m} s: {this.state.time.s}
      </div>
    );
  }
}

ReactDOM.render(<Timer/>, document.getElementById('container'));
    <script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react.min.js"></script>
     <script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="container"></div>

I put a button here to start the process, but if you want to start it once the ponent renders, just put this.startTimer(); inside of the ponentDidMount lifecycle

本文标签: javascriptCreating a timer in reactStack Overflow