FCC Pomodoro Clock无法通过内置测试

时间:2019-09-05 03:50:58

标签: javascript reactjs

我正在做FCC锻炼Pomodoro Clock这样的工作。我认为这是一个问题,当倒数计时在最后一分钟时,暂停将不起作用。顺便说一句,暂停可以在最后一分钟生效。我怀疑此问题会导致其他测试失败。还要测试说重置功能不起作用,但可以起作用。

编辑: 我的问题的更深层解释: 方法 paused()用于将状态 paused false 更改为 true ,反之亦然。该方法在 timer()方法内调用,该方法的作用还在于启动 setInterval 。在变量 this.clearTimer 中设置了 setInterval 。如果已暂停为true,则会启动 clearInterval()并停止计时器。

点击具有 start_stop id 的元素,已暂停进行更改,就会进行评估,就像我之前所说的那样;假=> setInterval去; true => setInterval停止。

未暂停时,从状态记录的值中设置setInterval。问题是,当您暂停计时器的时间少于一分钟(59、58等)时,即使暂停状态从“真”更改为“假”,它也不会恢复倒计时?如果倒数计时超过1分钟,则开始/暂停会按规定进行。

您可以在以下位置查看代码:my pen

这是主要组件的代码(来自本地):

    import React from 'react';
    import ReactDOM from 'react-dom';
    import 'bootstrap/dist/css/bootstrap.css';
    import './style.scss';
    import Item from "./item.js";
    import Arrow from "./arrow.js";
    import Fa from "./fa.js";

    class Pomodoro  extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            breakLength: 5,
            multiplier: 25,
            base: 1000,
            time: 0,
            minutes: 25,
            seconds: 60,
            paused: false,
            session: false,
            break: true,
            disabled: false,
        };
        this.increment = this.increment.bind(this);
        this.decrement = this.decrement.bind(this);
        this.timer = this.timer.bind(this);
        this.reset = this.reset.bind(this);
        this.paused = this.paused.bind(this);
        this.myRef = React.createRef();
        this.clearTimer = null;
    }

    componentDidMount(){

        this.setState({
            time: ""+(this.state.multiplier*this.state.base*60),
            minutes: this.state.multiplier,
            seconds: this.state.seconds === 60 ? "00" : this.state.seconds ,
        });

    }

    paused(){

        this.setState({
            paused: !this.state.paused
        });

    }

    timer(){

        this.paused();

        if(this.state.paused === false){

            if((this.state.minutes!==0 && this.state.seconds!==0) || (this.state.seconds!==0)){

                this.clearTimer = setInterval(() => {

                    if(this.state.session===false){
                        console.log("Sada ide session.");

                        this.setState({
                            time: ""+(this.state.time-this.state.base),
                            minutes: this.state.minutes > 0 ? Math.floor(((this.state.time-this.state.base)/(this.state.base))/60) : this.state.minutes,
                            seconds: this.state.seconds > 0 ? this.state.seconds-1 : 59,
                            session: (this.state.minutes===0 && this.state.seconds===1) ? true : false,
                            break: (this.state.minutes===0 && this.state.seconds===1) ? false : true,
                        });

                    }

                    if(this.state.break===false && this.state.session===true && this.state.time==="0"){
                        console.log("Kraj session-a. Sada ide resetovanje, pa break.");
                        this.setState({
                            time: ""+(this.state.breakLength*this.state.base*60),
                            minutes: this.state.breakLength,
                            seconds: 60,
                        });

                    }

                    if(this.state.break===false){
                        console.log("Sada ide break.");
                        this.setState({
                            time: ""+(this.state.time-this.state.base),
                            minutes: this.state.minutes > 0 ? Math.floor(((this.state.time-this.state.base)/(this.state.base))/60) : this.state.minutes,
                            seconds: this.state.seconds > 0 ? this.state.seconds-1 : 59,
                            session: (this.state.minutes===0 && this.state.seconds===1) ? false : true,
                            break: (this.state.minutes===0 && this.state.seconds===1) ? true : false,
                        });

                    }

                    if(this.state.break===true && this.state.session===false && this.state.time==="0"){
                        console.log("Kraj break-a. Sada ide resetovanje, pa session.");
                        this.setState({
                            time: ""+(this.state.multiplier*this.state.base*60),
                            minutes: this.state.multiplier,
                            seconds: this.state.seconds === 60 ? "00" : this.state.seconds,
                        });

                    }

                }, this.state.base);

            }

        }
        else{

            clearInterval(this.clearTimer);

        }

    }

    reset(){

        this.myRef.current.pause();
        this.myRef.current.currentTime = 0;
        clearInterval(this.clearTimer);
        this.clearTimer = null;
        this.setState({
            breakLength: 5,
            multiplier: 25,
            base: 1000,
            time: ""+(25*1000*60),
            minutes: 25,
            seconds: 60,
            paused: false,
            session: false,
            break: true,
            disabled: false,
        });

    }

    increment(e){

        console.log(e.target.id);
        let myId = e.target.id;

        if(myId==="break-increment"){

            this.setState({
                breakLength: this.state.breakLength <60 ? this.state.breakLength+1 : this.state.breakLength,
            });

        }
        else if(myId==="session-increment"){

            this.setState({
                multiplier: this.state.multiplier < 60 ? this.state.multiplier+1 : this.state.multiplier,
                time: this.state.time !== "60" ? ""+((this.state.multiplier+1)*this.state.base*60) : this.state.time,
                minutes: this.state.minutes < 60 ? this.state.multiplier+1 : this.state.minutes,
            });

        }

    }

    decrement(e){

        console.log(e.target.id);
        let myId = e.target.id;

        if(myId==="break-decrement" && this.state.breakLength > 1){

            this.setState({
                breakLength: this.state.breakLength > 1 ? this.state.breakLength-1 : this.state.breakLength,
            });

        }
        else if(myId==="session-decrement" && this.state.multiplier > 1 && this.state.time > 1 && this.state.minutes > 1){

            this.setState({
                multiplier: this.state.multiplier > 1 ? this.state.multiplier-1 : this.state.multiplier,
                time: this.state.time > 1 ? (""+((this.state.multiplier-1)*this.state.base*60)) : this.state.time,
                minutes: this.state.minutes > 1 ? this.state.multiplier-1: this.state.minutes,
            });

        }

    }

    render(){
        //console.log(this.state);

        const minutes = (""+this.state.minutes).length===1 ? "0"+this.state.minutes : this.state.minutes;
        const seconds = this.state.seconds===60 ? "00" : ((""+this.state.seconds).length===1 ? "0"+this.state.seconds : this.state.seconds);
        const time = minutes+":"+seconds;

        if(time==="00:00"){
            console.log("1: "+time);
            console.log("2: "+this.state.minutes+":"+this.state.seconds);
            this.myRef.current.play();
        }

        /*if((this.state.minutes+":"+this.state.seconds)===time){
            alert("alert2: "+this.state.minutes+":"+this.state.seconds);
        }*/

        const lastSesMin = (minutes==="00") ? {color: 'red',} : {};

        const decrement = this.clearTimer ? ()=>{} : this.decrement;
        const increment = this.clearTimer ? ()=>{} : this.increment;

        const item2Head = <h3 id="break-label">Break Length</h3>;
        const fa1 = <Fa klasa={"fa fa-arrow-down fa-2x"} id={"break-decrement"} onClick={decrement}/>;
        const fa2 = <Fa klasa={"fa fa-arrow-up fa-2x"} id={"break-increment"} onClick={increment}/>;
        const arr1 = [<Arrow klasa={"arrow"} key={0} arrow={item2Head}/>, <br key={1}/>, <Arrow klasa={"arrow"} key={2} arrow={fa1}/>, <Arrow id={"break-length"} klasa={"nums"} key={3} arrow={this.state.breakLength}/> , <Arrow key={4} klasa={"arrow"} arrow={fa2}/>];

        const item3Head = <h3 id="session-label">Session Length</h3>;
        const fa3 = <Fa klasa={"fa fa-arrow-down fa-2x"} id={"session-decrement"} onClick={decrement}/>;
        const fa4 = <Fa klasa={"fa fa-arrow-up fa-2x"} id={"session-increment"} onClick={increment}/>;
        const arr2 = [<Arrow klasa={"arrow"} key={0} arrow={item3Head}/>, <br key={1}/>, <Arrow klasa={"arrow"} key={2} arrow={fa3}/>, <Arrow klasa={"nums"} id={"session-length"} key={3} arrow={this.state.multiplier}/> , <Arrow key={4} klasa={"arrow"} arrow={fa4}/>];

        const countdownLabel = (this.state.session===false &&  this.state.break===true) ? "Session" : "Break";
        const item4Head = <h3 key={0} id={"timer-label"} style={lastSesMin}>{countdownLabel}</h3>;
        const nums2 = <div key={1} className="nums" style={lastSesMin} id={"time-left"}>{time}</div>;
        const arr3 = [item4Head, nums2];

        const fa5 = <Fa key={0} klasa={"fa fa-play arrow controls"} title={"start-pause"}/>;
        const fa6 = <Fa key={1} klasa={"fa fa-pause arrow controls"} title={"start-pause"}/>;
        const fa7 = <Fa key={2} klasa={"fa fa-refresh arrow controls"} id="reset" title={"reset"} onClick={this.reset}/>;
        const startPause = <div id="start_stop" key={4} onClick={this.timer}>{fa5}{fa6}</div>;
        const arr4 = [startPause, fa7];

        return(
            <div className="grid-container cent">

                <Item klasa={"item1"} arrowsAndNums={"Pomodoro Clock"}/>

                <Item klasa={"item2"} arrowsAndNums={arr1}/>

                <Item klasa={"item3"} arrowsAndNums={arr2}/>

                <Item klasa={"item4"} arrowsAndNums={arr3}/>

                <Item klasa={"item4"} arrowsAndNums={arr4}/>
                <audio ref={this.myRef} id="beep" src="http://soundbible.com/grab.php?id=2158&type=wav"></audio>
            </div>
        );

    }

}

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

Edit2: 我解决了暂停问题。我刚刚将行if(this.state.minutes!==0 && this.state.seconds!==0){更改为行if((this.state.minutes!==0 && this.state.seconds!==0) || (this.state.seconds!==0)){

有些图像即使没有错误也显示测试错误。 PS:为了对练习进行错误测试,已使用fcc的错误测试脚本来生成这些假定的错误。

Image1的错误: Image1 of the errors

Image2的错误: enter image description here

Edit3: 还有测试套件中可能出现的错误图像: enter image description here

编辑4: 正如我最近发现的那样,甚至FCC的番茄酱有时也无法通过测试。有时在不同的浏览器中,有时在星期日,有时等。 长话短说,我设计了新代码,解决了以前所做的编辑中的问题。但是,它仍然遭受与上述FCC番茄番茄相同的问题。我在某处读到一些有关定时事件的信息,定时事件的实现和执行取决于浏览器,在这种情况下,测试套件本身也是如此,我的pomodoro也是如此。因此应该存在各种“冲突” ...解决这些不一致和冲突,以便在每个浏览器中的每项诉讼/测试中,看起来都是无法克服的,我想会...我想问的是,可以吗?如果我提交我的番茄,关于所说的一切? 我的改进pomodoro 添加了注释,这些注释解释了代码的功能,因此可以随意浏览。 我的测试图片未通过:failed test 在左上方,您可以看到28/29通过的测试。 注意::您可以在Codepen中看到更新的代码以及注释。

1 个答案:

答案 0 :(得分:1)

只需阅读一些代码(无需测试您的沙盒),但是我发现一个问题,由于可能的竞争条件,该问题可能导致一些难以理解的错误:

paused(){

    this.setState({
        paused: !this.state.paused
    });

}

timer(){

    this.paused();

    if(this.state.paused === false){

    // ...

timer()正在调用paused,状态在此更新。然后timer()检查新状态。

问题:

  1. setState may be batched,因此对paused的连续调用可能基于某个值。

解决方案:使用功能作为状态更新程序:

setState(prevState => ({
    paused: !prevState.paused
}));
  1. setState may be asynchronous(相同链接),因此当在paused中读取时,timer()的状态可能无法反映该更改!

解决方案:将回调用作setState()的第二个参数:

setState(prevState => ({
    paused: !prevState.paused
}), () => {
    // You can read updated this.state.paused here....
});

因此整个代码片段可以这样实现:

 paused(callback){
    setState(prevState => ({
        paused: !prevState.paused
    }), callback);
}

timer(){

    this.paused(() => {

        // if(this.state.paused === false){
        // ...
    });

但是,如果您有很多这样的地方,则可以快速进入某种回调地狱。并且,与这些状态变化并行地管理计时器的启动/停止可能变得非常棘手。您至少可以简化计时器,但让其始终运行(从componentDidMount()开始,在componentWillUnmount()停止),并决定每个滴答应做什么(例如,在“暂停”时什么也不做,或者让“暂停”指示灯闪烁...)

侧面说明:像base这样的变量不需要处于状态,因为它们只是内部变量,不需要触发组件的重新呈现。