【问题标题】:React: How can this Pomodoro Clock pass the `freecodecamp tests 12 and 13`?React:这个番茄钟如何通过“freecodecamp 测试 12 和 13”?
【发布时间】:2023-03-13 08:43:01
【问题描述】:

在这个 React Pomodoro 时钟中,setInterval 在每次减少 state.timeLeftSeconds 时调用 down。但是,当试图通过 freecodecamp 测试 12 和 13 时,会出现一条消息:Break time didn't start with the correct value.: expected 60 to be at most 5。然而,当时钟运行时,代码似乎运行良好。任何帮助将不胜感激。测试套件也在代码沙箱中:https://codesandbox.io/s/xenodochial-fog-981wl

index.js

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

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

const states = [ { name: 'session', duration: 1500 }, { name: 'break', duration: 300 } ]

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

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() {
    let sessionSeconds = this.state.sessionLength * 60;
    this.setState({ timeLeftSeconds: sessionSeconds });
    this.setState({ timeLeft: secondsToMins(sessionSeconds) });
  }

  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
    let breakLength = this.state.breakLength - 1;
    if (breakLength > 0 && breakLength < 61){
      this.setState({ breakLength: breakLength });
      let breakSeconds = breakLength * 60;
      states[1]['duration'] = breakSeconds;
    }
  }

  breakIncrement() {
    // same as decrement except does increment
    let breakLength = this.state.breakLength + 1;
    if (breakLength > 0 && breakLength < 61){
      this.setState({ breakLength: breakLength });
      let breakSeconds = breakLength * 60;
      states[1]['duration'] = breakSeconds;
    }
  }

  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
    let sessionLength = this.state.sessionLength - 1;
    if (sessionLength > 0 && sessionLength < 61){
      states[0]['duration'] = sessionLength*60;
      this.setState(prevState => ({
        sessionLength: prevState.sessionLength-1,
        timeLeftSeconds: (prevState.sessionLength-1)*60,
        timeLeft:  secondsToMins((prevState.sessionLength-1)*60)})
      );
    }
  }

  sessionIncrement() {
    // same as decrement except does increment
    let sessionLength = this.state.sessionLength + 1;
    if (sessionLength > 0 && sessionLength < 61){
      states[0]['duration'] = sessionLength*60;
      this.setState(prevState => ({
        sessionLength: prevState.sessionLength+1,
        timeLeftSeconds: (prevState.sessionLength+1)*60,
        timeLeft:  secondsToMins((prevState.sessionLength+1)*60)})
      );
    }
  }

  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() {
    let intervalFunc = this.state.intervalFunc;
    clearInterval(intervalFunc);
    // reset state to default values
    this.setState({ breakLength: 5 });
    this.setState({ sessionLength: 25 });
    this.setState({ init: 'session' });
    this.setState({ timeLeftSeconds: 1500})
    this.setState({ timeLeft: '25:00' });
    this.setState({ stateIndex: 0 });
    this.setState({ started: false });
    this.setState({ intervalFunc: undefined });
  }


  decreaseCurrentSecond = () => this.state.timeLeftSeconds--;

  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.decreaseCurrentSecond()),
      1000
    );
    this.setState({ intervalFunc: intervalFunc });


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

      let sound = document.getElementById(id).childNodes[0];

      if (time <= 0 ) {
        sound.play();
        this.setState({ timeLeft: secondsToMins(time) });

        console.log(time);
        console.log(this.stateIndex);
        console.log(this.state.init);
        console.log(this.state.timeLeftSeconds);

        let stateIndex = (this.state.stateIndex + 1) % states.length;
        this.setState({ stateIndex: stateIndex });
        this.setState({ init: states[stateIndex]['name'] });
        this.setState({ timeLeftSeconds: states[stateIndex]['duration'] });

        console.log(time);
        console.log(this.stateIndex);
        console.log(this.state.init);
        console.log(this.state.timeLeftSeconds);
      }
    };

    down(this.decreaseCurrentSecond());
  }

  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>
  </body>
  </html>

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    这些测试失败的原因是,一旦时钟进入“中断”周期,状态中的timeLeftSeconds 设置不正确,反之亦然。我在下面做了必要的调整。注意decreaseCurrentSeconddown 方法的变化。

    此外,永远不要尝试像您在此处所做的那样直接更新状态中的值 - decreaseCurrentSecond = () =&gt; this.state.timeLeftSeconds--;。这不是好的做法 - Why can't I directly modify a component's state, really?

    import React from "react";
    import ReactDOM from "react-dom";
    import "./style.css";
    
    /*
     * A simple React component
     */
    const initState = {
      breakLength: 0.5,
      sessionLength: 0.5,
      init: "session",
      // stateIndex: 0,
      timeLeft: undefined,
      timeLeftSeconds: undefined,
      started: false,
      intervalFunc: undefined
    };
    
    const secondsToMins = (time) => {
      let converted =
        ("0" + Math.floor(time / 60)).slice(-2) +
        ":" +
        ("0" + Math.floor(time % 60)).slice(-2);
      return converted;
    };
    
    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() {
        let sessionSeconds = this.state.sessionLength * 60;
        this.setState({ timeLeftSeconds: sessionSeconds });
        this.setState({ timeLeft: secondsToMins(sessionSeconds) });
      }
    
      breakDecrement() {
        console.log("break decrement");
        // decrements the breakLength and the breakSeconds
        // breakLength is only a number ie. 5 (does not show seconds)
        // breakSeconds is that nunber converted into seconds
        let breakLength = this.state.breakLength - 1;
        if (breakLength > 0 && breakLength < 61) {
          this.setState({ breakLength: breakLength });
          // let breakSeconds = breakLength * 60;
          // states[1]["duration"] = breakSeconds;
        }
      }
    
      breakIncrement() {
        // same as decrement except does increment
        let breakLength = this.state.breakLength + 1;
        if (breakLength > 0 && breakLength < 61) {
          this.setState({ breakLength: breakLength });
          // let breakSeconds = breakLength * 60;
          // states[1]["duration"] = breakSeconds;
        }
      }
    
      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
        let sessionLength = this.state.sessionLength - 1;
        if (sessionLength > 0 && sessionLength < 61) {
          // states[0]["duration"] = sessionLength * 60;
          this.setState((prevState) => ({
            sessionLength: prevState.sessionLength - 1,
            timeLeftSeconds: (prevState.sessionLength - 1) * 60,
            timeLeft: secondsToMins((prevState.sessionLength - 1) * 60)
          }));
        }
      }
    
      sessionIncrement() {
        // same as decrement except does increment
        let sessionLength = this.state.sessionLength + 1;
        if (sessionLength > 0 && sessionLength < 61) {
          // states[0]["duration"] = sessionLength * 60;
          this.setState((prevState) => ({
            sessionLength: prevState.sessionLength + 1,
            timeLeftSeconds: (prevState.sessionLength + 1) * 60,
            timeLeft: secondsToMins((prevState.sessionLength + 1) * 60)
          }));
        }
      }
    
      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.setState({ started: true });
          this.countDown(id);
        }
        // pauses the countDown
        if (this.state.started) {
          this.setState({ started: false });
          let intervalFunc = this.state.intervalFunc;
          clearInterval(intervalFunc);
        }
      }
    
      reset() {
        let intervalFunc = this.state.intervalFunc;
        clearInterval(intervalFunc);
        // reset state to default values
        this.setState({ breakLength: 5 });
        this.setState({ sessionLength: 25 });
        this.setState({ init: "session" });
        this.setState({ timeLeftSeconds: 1500 });
        this.setState({ timeLeft: "25:00" });
        // this.setState({ stateIndex: 0 });
        this.setState({ started: false });
        this.setState({ intervalFunc: undefined });
      }
    
      decreaseCurrentSecond = () =>
        //this.state.timeLeftSeconds--;
        {
          this.setState({
            timeLeftSeconds: this.state.timeLeftSeconds - 1
          });
          return this.state.timeLeftSeconds;
        };
    
      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.decreaseCurrentSecond()),
          1000
        );
        this.setState({ intervalFunc: intervalFunc });
    
        const down = (time) => {
          if (time > 0) {
            // converts seconds to MM:SS at every t-minus
            this.setState({ timeLeft: secondsToMins(time) });
            /*
            console.log(time);
            console.log(this.state.timeLeft);*/
          }
    
          let sound = document.getElementById(id).childNodes[0];
    
          if (time <= 0) {
            sound.play();
            this.setState({ timeLeft: secondsToMins(time) });
    
            console.log(`##########`);
            console.log(time);
            // console.log(this.stateIndex);
            console.log(this.state.init);
            console.log(this.state.timeLeftSeconds);
            console.log(`##########`);
    
            // let stateIndex = (this.state.stateIndex + 1) % states.length;
            // this.setState({ stateIndex: stateIndex });
            this.setState({
              init: this.state.init === "session" ? "break" : "session"
            });
            this.setState({
              timeLeftSeconds:
                this.state.init === "session"
                  ? this.state.sessionLength * 60 + 1
                  : this.state.breakLength * 60 + 1
            });
    
            console.log(`##########`);
            console.log(time);
            // console.log(this.stateIndex);
            console.log(this.state.init);
            console.log(this.state.timeLeftSeconds);
            console.log(`##########`);
          }
        };
    
        // down(this.decreaseCurrentSecond());
      }
    
      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"));
    

    https://codesandbox.io/s/heuristic-star-i3t8m?file=/src/index.js

    【讨论】:

      猜你喜欢
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多