Реакция: часы Pomodoro: как может продолжаться обратный отсчет?

#javascript #reactjs

#javascript #reactjs

Вопрос:

В этих часах Pomodoro есть функция countDown(id) , которая ведет обратный отсчет по секундам и преобразует оставшиеся секунды в формат MM: SS при каждом t-минус. Однако он ведет обратный отсчет только до 24:59, а затем при каждом последующем t-минус он остается на уровне 24:59. Любая помощь была бы очень признательна.

index.js

 import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';

/*
* A simple React component
*/
const initState = {
  breakLength: 5,
  breakSeconds: undefined,
  sessionLength: 25,
  sessionSeconds: undefined,
  init: 'session',
  timeLeft: undefined,
  started: false,
  intervalFunc: undefined
}

class Clock extends React.Component {

  constructor(props) {
    super(props);
    this.state = initState;
    this.breakDecrement = this.breakDecrement.bind(this);
    this.breakIncrement = this.breakIncrement.bind(this);
    this.sessionDecrement = this.sessionDecrement.bind(this);
    this.sessionIncrement = this.sessionIncrement.bind(this);
    this.startStop = this.startStop.bind(this);
    this.reset = this.reset.bind(this);
  }

  componentDidMount() {
    // seconds are used for the countDown()
    // seconds are converted to MM:SS at every t-minus
    let breakSeconds = this.state.breakLength * 60;
    let sessionSeconds = this.state.sessionLength * 60;

    let time = sessionSeconds;

    // Convert to MM:SS
    const secondsToMins = (time) => {
      let converted = Math.floor(time / 60)   ':'   ('0'   Math.floor(time % 60)).slice(-2);
      return converted;
    }

    // Initialize everything
    this.setState({ breakSeconds: breakSeconds }, () => console.log(this.state.breakSeconds));
    this.setState({ sessionSeconds: sessionSeconds }, () => console.log(this.state.sessionSeconds));
    this.setState({ timeLeft: secondsToMins(time) }, () => console.log(this.state.timeLeft));
  }

  breakDecrement() {
    // decrements the breakLength and the breakSeconds
    // breakLength is only a number ie. 5 (does not show seconds)
    // breakSeconds is that nunber converted into seconds
    const toSeconds = () => {
      let breakSeconds = this.state.breakLength * 60;
      this.setState({ breakSeconds: breakSeconds }, () => console.log(this.state.breakSeconds));
    }

    let breakLength = this.state.breakLength - 1;
    this.setState({ breakLength: breakLength }, toSeconds );

  }

  breakIncrement() {
    // same as decrement except does increment
    const toSeconds = () => {
      let breakSeconds = this.state.breakLength * 60;
      this.setState({ breakSeconds: breakSeconds }, () => console.log(this.state.breakSeconds));
    }

    let breakLength = this.state.breakLength   1;
    this.setState({ breakLength: breakLength }, toSeconds);

  }

  sessionDecrement() {
    // decrements the sessionLength and the sessionSeconds
    // sessionLength is only a number ie. 25 (does not show seconds)
    // sessionSeconds is that nunber converted into seconds
    const toSeconds = () => {
      let sessionSeconds = this.state.sessionLength * 60;
      this.setState({ sessionSeconds: sessionSeconds }, () => console.log(this.state.sessionSeconds));
      this.setState({timeLeft: sessionSeconds});
    }

    let sessionLength = this.state.sessionLength - 1;
    this.setState({ sessionLength: sessionLength }, toSeconds);


  }
  sessionIncrement() {
    // same as decrement except does increment
    const toSeconds = () => {
      let sessionSeconds = this.state.sessionLength * 60;
      this.setState({ sessionSeconds: sessionSeconds }, () => console.log(this.state.sessionSeconds));
      this.setState({timeLeft: sessionSeconds});
    }

    let sessionLength = this.state.sessionLength   1;
    this.setState({ sessionLength: sessionLength }, toSeconds);
  }

  startStop(id) {
    // starts the countDown, which runs continuously until the start/stop button
    // is pressed again, which pauses the countdown.
    // the id parameter is used by countDown to play the audio beep
    if(!this.state.started){
      this.countDown(id);
      this.setState({ started: true});
    }
    // pauses the countDown
    if(this.state.started){
      let intervalFunc = this.state.intervalFunc;
      clearInterval(intervalFunc);
      this.setState({ started: false});
    }
  }

  reset() {
    // reset state to default values
    this.setState({ breakLength: 5 });
    this.setState({ sessionLength: 25 });
    this.setState({ init: 'session' });
    this.setState({ timeLeft: '25:00' });
  }

  countDown(id){

    // set the function to a variable and set state to it, so the function
    // can be paused with clearInterval()
    var intervalFunc = setInterval(() => down(this.state.sessionSeconds - 1), 1000);
    this.setState({intervalFunc: intervalFunc});

    // seconds are converted to MM:SS at every t-minus
    const down = (tMinus) =>
    {
      let time = tMinus;
      const secondsToMins = (time) => {
        let converted = Math.floor(time / 60)   ':'   ('0'   Math.floor(time % 60)).slice(-2);
        return converted;
      }

      // converts seconds to MM:SS at every t-minus
      this.setState({ timeLeft: secondsToMins(time) }, () => console.log(this.state.timeLeft));

      // when sessionSeconds reaches 0, start the break
      if(this.state.sessionSeconds == 0 amp;amp; this.state.init == 'session'){
        let sound = document.getElementById(id).childNodes[0];
        sound.play();
        let breakSeconds = this.state.breakSeconds;
        this.setState({ init: 'break' });
        this.setState({ timeLeft: secondsToMins(breakSeconds) });
      }

      // when breakSeconds reaches 0, start the session
      if(this.state.timeLeft == 0 amp;amp; this.state.init == 'break'){
        let sound = document.getElementById(id).childNodes[0];
        sound.play();
        let sessionSeconds = this.state.sessionSeconds;
        this.setState({ init: 'session' });
        this.setState({ timeLeft: secondsToMins(sessionSeconds) });
      }
    }
  }


  render() {
    return (
      <div id="clock">
      <h1 id="title">25-5 Clock</h1>

      <div>
      <p id="break-label">Break Length</p>
      <p id="break-length">{this.state.breakLength}</p>
      <button id="break-decrement" onClick={e => this.breakDecrement()}> Decrease </button>
      <button id="break-increment" onClick={e => this.breakIncrement()}> Increase </button>
      </div>

      <div>
      <p id="session-label">Session Length</p>
      <p id="session-length">{this.state.sessionLength}</p>
      <button id="session-decrement" onClick={e => this.sessionDecrement()}> Decrease </button>
      <button id="session-increment" onClick={e => this.sessionIncrement()}> Increase </button>
      </div>

      <hr/>

      <div>
      <p id="timer-label">{this.state.init}</p>
      <p id="time-left">{this.state.timeLeft}</p>
      <button id="start_stop" onClick={e => this.startStop(e.target.id)}><audio id="beep" src='./beep.mp3'></audio> start/stop </button>
      <button id="reset" onClick={e => this.reset()}> reset </button>
      </div>

      </div>
    );
  }
};

/*
* Render the above component into the div#app
*/
ReactDOM.render(<Clock />, document.getElementById("app"));
  

index.html

 <!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>25-5 Clock</title>
  <style>
  </style>
</head>
<body>
  <main>
    <div id="app"></app>
    </main>
  <script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
  </body>
  </html>
  

Ответ №1:

Это из-за этого утверждения здесь — var intervalFunc = setInterval(() => down(this.state.sessionSeconds - 1), 1000); вы всегда отправляете одно и то же значение. Состояние sessionSeconds in никогда не меняется, и вы всегда вычитаете из него только 1 за каждый интервал.

Что вы можете сделать, это добавить sessionSecondsExpired: 0 свойство к состоянию для отслеживания истекших секунд в текущем сеансе. Затем измените свою countDown функцию следующим образом, чтобы вычесть столько секунд из sessionSeconds :

 countDown(id) {
    var intervalFunc = setInterval(() => {
      down(this.state.sessionSeconds - this.state.sessionSecondsExpired);
    }, 1000);
    this.setState({ intervalFunc: intervalFunc });

    // seconds are converted to MM:SS at every t-minus
    const down = (tMinus) => {
      this.setState({
        sessionSecondsExpired: this.state.sessionSecondsExpired   1
      });
      .....
      .....
      .....
    };
  }
  

Также не забудьте «сбросить» с this.setState({ sessionSecondsExpired: 0 }); reset помощью функции in.

Это было быстро. Возможно, это нарушило некоторые другие функции, я не уверен, но вы поняли идею. Вот песочница, если вы хотите взглянуть — https://codesandbox.io/s/great-gould-6zmo7