Javascript 如何在计时器倒计时为0时触发事件

Javascript 如何在计时器倒计时为0时触发事件,javascript,reactjs,timer,Javascript,Reactjs,Timer,我有一个父组件,其中包含计时器组件。计时器从15分钟开始,倒计时到0。当我的计时器显示时间为0时,我想触发提交按钮事件,提交按钮位于测验组件内(测验组件也是父组件的子组件)。我发现当p标记改变时,我可能可以使用MutationObserver。我不确定这是正确的、唯一的方法还是有更好的方法来实现这一点 父组件: import React, { Component } from 'react'; import '../css/App.css' import Quiz from './Quiz';

我有一个父组件,其中包含计时器组件。计时器从15分钟开始,倒计时到0。当我的计时器显示时间为0时,我想触发提交按钮事件,提交按钮位于测验组件内(测验组件也是父组件的子组件)。我发现当p标记改变时,我可能可以使用MutationObserver。我不确定这是正确的、唯一的方法还是有更好的方法来实现这一点

父组件:

import React, { Component } from 'react';
import '../css/App.css'
import Quiz from './Quiz';
import Timer from './Timer';
import { connect } from 'react-redux';
import { ActionTypes } from '../redux/constants/actionTypes';
import { saveQuizAll, getQuizIndex } from '../commonjs/common.js';

const mapStateToProps = state => { return { ...state.quiz, ...state.quizAll } };

const mapDispatchToProps = dispatch => ({
  onQuizLoad: payload => dispatch({ type: ActionTypes.QuizLoad, payload }),
  onQuizChange: payload => dispatch({ type: ActionTypes.QuizAnswerAll, payload }),
  onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
});

class QuizContainer extends Component {
  state = {
    quizes: [
      { id: 'data/class1.json', name: 'Class 1' },
      { id: 'data/class2.json', name: 'Class 2' },
      { id: 'data/class3.json', name: 'Class 3' },
      { id: 'data/class4.json', name: 'Class 4' },
    ],
    quizId: 'data/class1.json'
  };

  pager = {
    index: 0,
    size: 1,
    count: 1
  }

  componentDidMount() {
    console.log('componentDidMount');
    this.load(this.state.quizId);
  }

  load(quizId, isValReload) {
    console.log('In load');
    let url = quizId || this.props.quizId;
    if (isValReload) {
      let quiz = this.props.quizAll.find(a => url.indexOf(`${a.id}.`) !== -1);
      console.log('In load quiz : ', quiz);
      this.pager.count = quiz.questions.length / this.pager.size;
      this.props.onQuizLoad(quiz);
      this.props.onPagerUpdate(this.pager);
    }
    else {
      fetch(`../${url}`).then(res => res.json()).then(res => {
        let quiz = res;
        quiz.questions.forEach(q => {
          q.options.forEach(o => o.selected = false);
        });
        quiz.config = Object.assign(this.props.quiz.config || {}, quiz.config);
        this.pager.count = quiz.questions.length / this.pager.size;
        this.props.onQuizLoad(quiz);
        this.props.onPagerUpdate(this.pager);
      });
    }
  }

  //This event implements restriction to change class without finishing curretnly selectd class
  onClassClick = (e) => {
    let qus = this.props.quiz.questions;
    // console.log(qus);
    let isNotAllAns = qus.some((q, i) => {
      var isNot = false;
      if (q.answerType.id !== 3 && q.answerType.id !== 4) {
        isNot = (q.options.find((o) => o.selected === true)) === undefined;
      }
      else {
        // console.log('q', q);
        isNot = ((q.answers === "" || q.answers.length === 0));
      }
      return isNot;
    });
    if (isNotAllAns) {
      alert('Please complete the quiz.');
      e.stopPropagation();
    }
  }

  /*
  saveQuizAll(_quizAll, _quiz) {
    let allQuiz = [];
    // , _quizAll, _quiz;
    // if (true) {
    //   _quiz = this.quiz;
    //   _quizAll = this.quizAll;
    // }

    console.log(this, _quiz, _quizAll);
    if (_quiz.questions.length !== 0) {

      if (_quizAll.length !== undefined) {
        console.log('Not Initial Setup Splice', _quiz.id);
        allQuiz = _quizAll;
        const qIndex = this.getQuizIndex(_quiz.id.toString());
        if (qIndex > -1) {
          allQuiz.splice(qIndex, 1, _quiz);
        }
        else {
          allQuiz.splice(_quizAll.length, 0, _quiz);
          // allQuiz.splice(this.props.quizAll.length-1, 0, this.props.quizAll, this.props.quiz);
        }
      }
      else {
        allQuiz[0] = _quiz;
      }
      return allQuiz;
      // if (true) {
      //   this.onQuizChange(allQuiz);
      // }
    }
  }
  */

  onChange = (e) => {
    // console.log(this.props.quizAll, this.props.quizAll.length);
    let allQuiz = [];
    allQuiz = saveQuizAll(this.props.quizAll, this.props.quiz);

    //below code converted into saveQuizAll funstion
    /*
    if (this.props.quizAll.length !== undefined) {
      console.log('Not Initial Setup Splice', this.props.quiz.id);
      allQuiz = this.props.quizAll;
      const qIndex = this.getQuizIndex(this.props.quiz.id.toString());
      if (qIndex > -1) {
        allQuiz.splice(qIndex, 1, this.props.quiz);
      }
      else {
        allQuiz.splice(this.props.quizAll.length, 0, this.props.quiz);
        // allQuiz.splice(this.props.quizAll.length-1, 0, this.props.quizAll, this.props.quiz);
      }
    }
    else {
      allQuiz[0] = this.props.quiz;
    }
    */

    // console.log('allQuiz Out - ', allQuiz);
    this.props.onQuizChange(allQuiz);
    console.log('Check QuizAll - ', this.props.quizAll);
    const aQuiz = JSON.parse(JSON.stringify(this.props.quizAll));
    this.setState({ quizId: e.target.value });
    if (aQuiz.length !== undefined && getQuizIndex(this.props.quizAll, e.target.value) > -1) {
      // console.log(aQuiz.findIndex(a => e.target.value.indexOf(`${a.id}.`) !== -1));
      this.load(e.target.value, true);
    }
    else {
      this.setState({ quizId: e.target.value });
      this.load(e.target.value, false);
    }
  }

  // getQuizIndex(qID) {
  //   return this.props.quizAll.findIndex(a => (qID.indexOf(`${a.id}.`) !== -1 || qID.indexOf(`${a.id}`) !== -1));
  // }

  render() {
    return (
      <div className="container">
        <header className="p-2">
          <div className="row">
            <div className="col-6">
              <h3>DADt Application</h3>

            </div>
            <div className="col-6 text-right">
              <label className="mr-1">Select Quiz:</label>
              <select onChange={this.onChange} onClick={this.onClassClick}>
                {this.state.quizes.map(q => <option key={q.id} value={q.id}>{q.name}</option>)}
              </select>
            </div>
          </div>
        </header>
        <Timer duration={900}/>
        <Quiz quiz={this.state.quiz} quizId={this.state.quizId} saveAll={saveQuizAll} mode={this.state.mode} />
      </div>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(QuizContainer);
import React, { Component } from 'react';
import { ActionTypes } from '../redux/constants/actionTypes';
import Review from './Review';
import Questions from './Questions';
import Result from './Result';
import { connect } from 'react-redux';
// import { saveQuizAll } from '../commonjs/common.js';


const mapStateToProps = state => { return { ...state.quiz, ...state.mode, ...state.pager, ...state.quizAll } };

const mapDispatchToProps = dispatch => ({
    onSubmit: payload => dispatch({ type: ActionTypes.QuizSubmit, payload }),
    onQuizChange: payload => dispatch({ type: ActionTypes.QuizAnswerAll, payload }),
    onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
});

class Quiz extends Component {
    move = (e) => {
        let id = e.target.id;
        let index = 0;
        if (id === 'first')
            index = 0;
        else if (id === 'prev')
            index = this.props.pager.index - 1;
        else if (id === 'next') {
            index = this.props.pager.index + 1;
          }
        else if (id === 'last')
            index = this.props.pager.count - 1;
        else
            index = parseInt(e.target.id, 10);

        if (index >= 0 && index < this.props.pager.count) {
            let pager = {
                index: index,
                size: 1,
                count: this.props.pager.count
            };
            this.props.onPagerUpdate(pager);
        }
    }

    saveStore(e) {
      let allQuiz = [];
      console.log(this, e);
      allQuiz = this.props.saveAll(e.props.quizAll, e.props.quiz);
      console.log(allQuiz);
      this.props.onQuizChange(allQuiz);
    }

    setMode = (e) => this.props.onSubmit(e.target.id);

    // setMode(e) {
    //   console.log('in mode',e);this.props.onSubmit(e.target.id);
    // }

    renderMode() {
      console.log('Inside here', this.props.mode);
        if (this.props.mode === 'quiz') {
            return (<Questions move={this.move} />)
        } else if (this.props.mode === 'review') {
            return (<Review quiz={this.props.quiz} move={this.move} />)
        } else {
            console.log('Before Results');
            const divSel = document.querySelector('div.col-6.text-right');
            // console.log('divSel', divSel);
            if (divSel) {
              divSel.style.display = "none";
            }
            return (<Result questions={this.props.quizAll || []} />)
        }
    }

    render() {
        return (
            <div>
                {this.renderMode()}
                {(this.props.mode !== 'submit') &&
                    <div>
                        <hr />
                        <button id="quiz" className="btn btn-primary" onClick={this.setMode}>Quiz</button>
                        <button id="review" className="btn btn-primary" onClick={this.setMode}>Review</button>
                        <button id="submit" className="btn btn-primary" onClick={(e) => {this.setMode(e); this.saveStore(this)}}>Submit Quiz</button >
                    </div >}
            </div>
        )
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Quiz);
import React,{Component}来自'React';
导入“../css/App.css”
从“./quick”导入测验;
从“./Timer”导入计时器;
从'react redux'导入{connect};
从“../redux/constants/ActionTypes”导入{ActionTypes};
从“../commonjs/common.js”导入{saveQuizAll,getQuizIndex};
const mapStateToProps=state=>{return{…state.quick,…state.quizAll};
const mapDispatchToProps=调度=>({
onQuizLoad:payload=>dispatch({type:ActionTypes.QuizLoad,payload}),
onQuizChange:payload=>dispatch({type:ActionTypes.quizanswrall,payload}),
onPagerUpdate:payload=>dispatch({type:ActionTypes.PagerUpdate,payload})
});
类QuizContainer扩展组件{
状态={
提问:[
{id:'data/class1.json',name:'Class 1'},
{id:'data/class2.json',name:'Class 2'},
{id:'data/class3.json',name:'class3'},
{id:'data/class4.json',name:'class4'},
],
quizId:'data/class1.json'
};
寻呼机={
索引:0,
尺寸:1,
计数:1
}
componentDidMount(){
log('componentDidMount');
this.load(this.state.quizId);
}
装载(quizId、isValReload){
console.log('In load');
让url=quizId | | this.props.quizId;
if(isValReload){
让quick=this.props.quizAll.find(a=>url.indexOf(`${a.id}.`)!=-1);
log('In load quick:',quick);
this.pager.count=quick.questions.length/this.pager.size;
这个.props.onQuizLoad(测验);
this.props.onPagerUpdate(this.pager);
}
否则{
获取(`../${url}`)。然后(res=>res.json())。然后(res=>{
让测验=res;
测验。问题。forEach(q=>{
q、 options.forEach(o=>o.selected=false);
});
quick.config=Object.assign(this.props.quick.config | |{},quick.config);
this.pager.count=quick.questions.length/this.pager.size;
这个.props.onQuizLoad(测验);
this.props.onPagerUpdate(this.pager);
});
}
}
//此事件实现了在不完成currentlyselectd类的情况下更改类的限制
onClassClick=(e)=>{
让qus=this.props.quick.questions;
//控制台日志(qus);
让isNotAllAns=qus.some((q,i)=>{
var isNot=false;
if(q.answerType.id!==3&&q.answerType.id!==4){
isNot=(q.options.find((o)=>o.selected==true))==未定义;
}
否则{
//console.log('q',q);
isNot=((q.answers==“”| | q.answers.length==0));
}
返回不是;
});
如果(isNotAllAns){
警惕('请完成测验');
e、 停止传播();
}
}
/*
saveQuizAll(_quizAll,_测验){
让所有测验=[];
//,quizAll,测验;
//如果(真){
//_quick=this.quick;
//_quizAll=this.quizAll;
// }
日志(这是一个测验,_quizAll);
如果(_quick.questions.length!==0){
如果(_quizAll.length!==未定义){
log('不是初始设置拼接',_quick.id);
所有测验=_quizAll;
const qIndex=this.getQuizIndex(_quick.id.toString());
如果(qIndex>-1){
所有测验.拼接(qIndex,1,_测验);
}
否则{
Allquick.拼接(_quizAll.length,0,_quick);
//allquick.splice(this.props.quizAll.length-1,0,this.props.quizAll,this.props.quizAll);
}
}
否则{
所有测验[0]=\u测验;
}
返回所有测验;
//如果(真){
//这个.onQuizChange(allquick);
// }
}
}
*/
onChange=(e)=>{
//log(this.props.quizAll,this.props.quizAll.length);
让所有测验=[];
allquick=saveQuizAll(this.props.quizAll,this.props.quick);
//下面的代码转换为saveQuizAll funstion
/*
if(this.props.quizAll.length!==未定义){
console.log('不是初始设置拼接',this.props.quick.id);
allquick=this.props.quizAll;
const qIndex=this.getQuizIndex(this.props.quick.id.toString());
如果(qIndex>-1){
allquick.splice(qIndex,1,this.props.quick);
}
否则{
allquick.splice(this.props.quizAll.length,0,this.props.quick);
//allquick.splice(this.props.quizAll.length-1,0,this.props.quizAll,this.props.quizAll);
}
}
否则{
allquick[0]=this.props.quick;
}
*/
//log('allquick Out-',allquick);
this.props.onQuizChange(allquick);
log('Check QuizAll-',this.props.QuizAll);
const-aQuiz=JSON.parse(JSON.stringify(this.props.quizAll));
this.setState({quizId:e.target.value});
if(aQuiz.length!==undefined&&getQuizIndex(this.props.quizAll,e.target.value)>-1){
//log(aQuiz.findIndex(a=>e.target.value.indexOf(`${a.id}.`)!=-1));
此.load(即目标值,true);
}
否则{
this.setState({quizId:e.target.value});
此.load(例如,target.value,false);
}
}
//getQuizIndex(qID){
//返回这个.props.quizAll.findIndex(a=>(qID.indexOf(`a.id}.`)!=-1 | | qID.indexOf(`a.id})!=-1);
// }
render(){
返回(
DADt应用
选择测验:
{this.state.quizes.map(q=>{q.name}}
);
}
}
导出默认连接(MapStateTops、mapDispatchToProps)(QuizContainer);
这是我的<
import React, { Component } from 'react';
import { ActionTypes } from '../redux/constants/actionTypes';
import Review from './Review';
import Questions from './Questions';
import Result from './Result';
import { connect } from 'react-redux';
// import { saveQuizAll } from '../commonjs/common.js';


const mapStateToProps = state => { return { ...state.quiz, ...state.mode, ...state.pager, ...state.quizAll } };

const mapDispatchToProps = dispatch => ({
    onSubmit: payload => dispatch({ type: ActionTypes.QuizSubmit, payload }),
    onQuizChange: payload => dispatch({ type: ActionTypes.QuizAnswerAll, payload }),
    onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
});

class Quiz extends Component {
    move = (e) => {
        let id = e.target.id;
        let index = 0;
        if (id === 'first')
            index = 0;
        else if (id === 'prev')
            index = this.props.pager.index - 1;
        else if (id === 'next') {
            index = this.props.pager.index + 1;
          }
        else if (id === 'last')
            index = this.props.pager.count - 1;
        else
            index = parseInt(e.target.id, 10);

        if (index >= 0 && index < this.props.pager.count) {
            let pager = {
                index: index,
                size: 1,
                count: this.props.pager.count
            };
            this.props.onPagerUpdate(pager);
        }
    }

    saveStore(e) {
      let allQuiz = [];
      console.log(this, e);
      allQuiz = this.props.saveAll(e.props.quizAll, e.props.quiz);
      console.log(allQuiz);
      this.props.onQuizChange(allQuiz);
    }

    setMode = (e) => this.props.onSubmit(e.target.id);

    // setMode(e) {
    //   console.log('in mode',e);this.props.onSubmit(e.target.id);
    // }

    renderMode() {
      console.log('Inside here', this.props.mode);
        if (this.props.mode === 'quiz') {
            return (<Questions move={this.move} />)
        } else if (this.props.mode === 'review') {
            return (<Review quiz={this.props.quiz} move={this.move} />)
        } else {
            console.log('Before Results');
            const divSel = document.querySelector('div.col-6.text-right');
            // console.log('divSel', divSel);
            if (divSel) {
              divSel.style.display = "none";
            }
            return (<Result questions={this.props.quizAll || []} />)
        }
    }

    render() {
        return (
            <div>
                {this.renderMode()}
                {(this.props.mode !== 'submit') &&
                    <div>
                        <hr />
                        <button id="quiz" className="btn btn-primary" onClick={this.setMode}>Quiz</button>
                        <button id="review" className="btn btn-primary" onClick={this.setMode}>Review</button>
                        <button id="submit" className="btn btn-primary" onClick={(e) => {this.setMode(e); this.saveStore(this)}}>Submit Quiz</button >
                    </div >}
            </div>
        )
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Quiz);
{ !(this.props.duration-this.state.seconds) && this.props.onTimeFinished() }
// state
state = {
    triggerSubmit: false
}  

// functions
doSubmit = () => {
    this.setState({ triggerSubmit: true });
}
resetSubmit = () => {
    this.setState({ triggerSubmit: false });
}  

// jsx
<Timer duration={900} doSubmit={this.doSubmit} />
<Quiz 
    quiz={this.state.quiz}
    quizId={this.state.quizId}
    saveAll={saveQuizAll}
    mode={this.state.mode}
    resetSubmit={this.resetSubmit}
    triggerSubmit={this.state.triggerSubmit} />
// function
doSubmit = (timeLeft) => {
  if (timeLeft === 0) {
    this.props.doSubmit();
  }
}  

// jsx
<p className="badge badge-success"
   onChange={() => {this.doSubmit(timeLeft)}>
   Time Left: {minutesDisplay}{secondsDisplay}
</p>
// state
state = {
    triggerSubmit: this.props.triggerSubmit
}  

// function
triggerSubmit = () => {
    if (this.state.triggerSubmit) {
       your trigger submit code here...
       this.props.resetSubmit();
    }
}
// ...
constructor(props) {
  // ...
  this.state = {
    timeExpired: false
  };
}
    
const onTimeExpired = () => {
  this.setState({timeExpired: true});
}
   
// ...
    
render() {
  return (
    <div className="container">
      { // ... }
      <Timer duration={900} onTimeExpired={onTimeExpired}/>
      <Quiz quiz={this.state.quiz} quizId={this.state.quizId} saveAll={saveQuizAll} mode={this.state.mode} triggerSubmit={this.state.timeExpired} />
      </div>
    );
}
// ...

componentDidUpdate() {
  if (this.state.seconds === this.props.duration) {
    this.props.onTimeExpired();
  }
}

// ...
// ...

componentDidUpdate() {
  if (this.props.triggerSubmit) {
    // Do whatever you do on submit
  }
}

// ...
// ...

componentDidUpdate() {
  if (this.state.seconds === this.props.duration) {
    const quizForm = document.getElementById('quizFormId');
    quizForm && quizForm.submit();
  }
}

// ...