仅在重构组件时,SetTimeout运行两次

时间:2018-07-17 08:25:23

标签: javascript reactjs firebase firebase-realtime-database

所以这很奇怪。首先,这是我的代码中负责超时的部分:

constructor(props) {
    super(props);
    this.state = {
        assignments: [],
        answers: [],
        uploadedFile: '',
        showTimeoutModal: false,
    };
    this.onImageDrop = this.onImageDrop.bind(this);
    this.startCountDown = this.startCountDown.bind(this);
    this.handleTimeoutButtonClick = this.handleTimeoutButtonClick.bind(this);
}


componentDidMount() {
    clearTimeout(this.state.timer);
    firebase.database().ref('Works').child(this.props.assignmentId).once('value', () => {
        // var startTime = r.val().acceptedDate.time;
        var startTime = Time.generate().time; //todo change to db specific time
        var waitingTime = Time.makeMinutes(5);
        var endTime = Time.generate().time + waitingTime;
        this.setState({timer: this.startCountDown(startTime, endTime)});
    }).catch(e => console.log(e));
}

componentWillUnmount() {
    clearTimeout(this.state.timer);
}

startCountDown(startTime, endTime) {
    var timeout = setTimeout(function () {
        var now = startTime;
        console.log(endTime - now);
        if (now >= endTime) {
            clearTimeout(timeout);
            console.log(true);
            this.setState({showTimeoutModal: true});
        } else {
            now = Time.generate().time;
            this.setState({timer: this.startCountDown(now, endTime)});
        }
    }.bind(this), 1000);
    return timeout;
}

handleTimeoutButtonClick() {
    var startTime = Time.generate().time;
    var waitingTime = Time.makeMinutes(0.15);
    var endTime = Time.generate().time + waitingTime;

    this.setState({showTimeoutModal: false, timer: this.startCountDown(startTime, endTime)});
}

我的项目的工作方式是:用户选择要解决的任务。我还不完全了解React,所以我不完全了解后台发生了什么。但是事实是,当我第一次构造组件时,一切都进行得很好。当我将组件移到其他路线时,所有组件也都可以工作。但是,如果我重新创建此组件,一切都会崩溃。

以下是全部内容:

import React from 'react';
import firebase from '../../firebase';
import {Col, Button, Modal} from 'react-bootstrap';
import Dropzone from 'react-dropzone';
import CancelAnswerButton from '../answer/CancelAnswerButton';
import FinishAnswerButton from '../answer/FinishAnswerButton';
import AnswerComponent from '../answer/AnswerComponent';
import MetaTags from 'react-meta-tags';
import {Redirect} from 'react-router';
import './AssignmentComponent.css';
import 'font-awesome/css/font-awesome.min.css';
import Time from '../globalMethods/Time';

export default class AssignmentComponent extends React.Component {
    async componentWillMount() {
        firebase.database().ref('Users').child(firebase.auth().currentUser.uid).on('value', snap => {
            if (snap.val().assignedWork) {
                this.setState({hasAssignedTask: true});
            } else {
                this.setState({hasAssignedTask: false});
            }
        });

        const assignmentsRef = firebase.database().ref('Works').orderByChild('firebaseKey').equalTo(this.props.assignmentId);
        await assignmentsRef.on('value', snapshot => {
            var assignments = snapshot.val();
            var newState = [];
            for (let assignment in assignments) {
                let currentAssignment = assignments[assignment];
                newState.push({
                    id: assignment,
                    category: currentAssignment.category,
                    level: currentAssignment.level,
                    pointAmount: currentAssignment.pointAmount,
                    pointBoost: currentAssignment.pointBoost,
                    points: currentAssignment.pointBoost + currentAssignment.pointAmount,
                    photoURL: currentAssignment.photoURL,
                    workText: currentAssignment.workText,
                    answers: currentAssignment.answers,
                });
            }
            var answers = [];
            for (let answer in newState[0].answers) {
                let currAns = newState[0].answers[answer];
                answers.push({
                    id: answer,
                    textAnswer: currAns.textAnswer,
                    downloadURL: currAns.downloadURL,
                })
            }
            this.setState({
                assignments: newState,
                answers: answers,
            });
        });
    }

    constructor(props) {
        super(props);
        this.state = {
            assignments: [],
            answers: [],
            uploadedFile: '',
            showTimeoutModal: false,
        };
        this.onImageDrop = this.onImageDrop.bind(this);
        this.startCountDown = this.startCountDown.bind(this);
        this.handleTimeoutButtonClick = this.handleTimeoutButtonClick.bind(this);
    }

    componentDidMount() {
        clearTimeout(this.state.timer);
        firebase.database().ref('Works').child(this.props.assignmentId).once('value', function () {
            // var startTime = r.val().acceptedDate.time;
            var startTime = Time.generate().time; //todo change to db specific time
            var waitingTime = Time.makeMinutes(5);
            var endTime = Time.generate().time + waitingTime;
            this.setState({timer: this.startCountDown(startTime, endTime)});
        }.bind(this)).catch(e => console.log(e));
    }

    componentWillUnmount() {
        clearTimeout(this.state.timer);
    }

    startCountDown(startTime, endTime) {
        var timeout = setTimeout(function () {
            var now = startTime;
            console.log(endTime - now);
            if (now >= endTime) {
                clearTimeout(timeout);
                console.log(true);
                this.setState({showTimeoutModal: true});
            } else {
                now = Time.generate().time;
                this.setState({timer: this.startCountDown(now, endTime)});
            }
        }.bind(this), 1000);
        return timeout;
    }

    handleTimeoutButtonClick() {
        var startTime = Time.generate().time;
        var waitingTime = Time.makeMinutes(0.15);
        var endTime = Time.generate().time + waitingTime;

        this.setState({showTimeoutModal: false, timer: this.startCountDown(startTime, endTime)});
    }

    async onImageDrop(files) {
        await this.setState({
            uploadedFile: files[0],
        });
        await AnswerComponent.handleImageSubmit(files[0], this.props);
    }

    render() {
        return (
            this.state.assignments.map(assignment => {
                return (
                    this.state.hasAssignedTask ?
                        <section key={assignment.id} className='display-assignment'>
                            <MetaTags>
                                <title>Zadanie</title>
                            </MetaTags>
                            <div style={{height: '150px', background: '#fff', borderBottom: '2px solid #e0e0e0'}}>
                                <div className='container'>
                                    <h4 style={{
                                        paddingTop: '75px',
                                        fontSize: '24px',
                                        textAlign: 'left'
                                    }}>Aktualnie rozwiązywane zadanie</h4>
                                </div>
                            </div>
                            <div className='wrapper show-grid task_info container-fluid task_solve_content'>
                                <Col xs={12} md={6}>
                                    <img className='task_img' alt='' src={assignment.photoURL}/>
                                    <p>{assignment.workText}</p>
                                </Col>
                                <Col xs={12} md={6}>
                                    <div className='row task_info_content'>
                                        <div className='col-sm-4 col-md-4 col-lg-4'>
                                            <h3 className='text-center gradient_text'>
                                                {assignment.category}
                                            </h3>
                                            <h4 className='text-center'>przedmiot</h4>
                                        </div>
                                        <div className='col-sm-4 col-md-4 col-lg-4'>
                                            <h3 className='text-center gradient_text'>
                                                {assignment.level}</h3>
                                            <h4 className='text-center'>poziom</h4>
                                        </div>
                                        <div className='col-sm-4 col-md-4 col-lg-4'>
                                            <h3 className='text-center gradient_text'>
                                                {assignment.points}</h3>
                                            <h4>punkty</h4>
                                        </div>
                                    </div>
                                    <form encType="multipart/form-data">
                                        <textarea placeholder='Wpisz rozwiązanie...' name="textAnswer" id="textAnswer"
                                                  style={{
                                                      width: '100%',
                                                      height: '80vh',
                                                      background: 'white',
                                                      color: 'black',
                                                  }}/>
                                        <div style={{width: '100%', height: '60px', position: 'relative'}}>
                                            <Button className="send_text_answer_button"
                                                    onClick={() => AnswerComponent.handleTextSubmit(this.props)}
                                                    style={{display: 'block'}}>
                                                <span>Wyslij odpowiedź tekstową</span>
                                            </Button>

                                        </div>
                                        <Dropzone
                                            className='dropzone'
                                            multiple={false}
                                            accept='image/*'
                                            style={{
                                                backgroundColor: '#fff',
                                                border: '1px solid #fff',
                                                borderBottom: '2px solid #e0e0e0',
                                                borderRadius: '4px',
                                                width: '100%',
                                                height: '300px',
                                                marginBottom: '20px'
                                            }}
                                            onDrop={this.onImageDrop.bind(this)}>

                                            <i className='fa fa-image' style={{
                                                fontSize: '64px',
                                                marginRight: '5px',
                                                color: '#e0e0e0',
                                                margin: 'auto',
                                                marginTop: '120px'
                                            }}/>
                                        </Dropzone>
                                        <h3 style={{display: 'none', color: 'red'}}
                                            id='error'>Upewnij sie ze dodana jest przynajmniej jedna odpowiedz!</h3>
                                        <div id='answers'>
                                            <h3 style={{
                                                fontSize: '18px',
                                                fontWeight: 'lighter',
                                                marginTop: '10px',
                                                marginBottom: '10px'
                                            }}>Odpowiedzi przeslane do tej pory</h3>
                                            {
                                                this.state.answers.map(answer => {
                                                    return (
                                                        <Col xs={12} md={12} key={answer.id}>
                                                            {
                                                                answer.textAnswer !== undefined &&
                                                                answer.textAnswer.length > 0 ?
                                                                    <div className='task_info_text_answer'>
                                                                        <p>{answer.textAnswer}</p>
                                                                    </div>
                                                                    :
                                                                    <img className="your_answer_img"
                                                                         src={answer.downloadURL}
                                                                         alt=""/>
                                                            }
                                                            <br/>
                                                            <Button className="delete_answer_button"
                                                                    onClick={() => AnswerComponent.removeAnswer(this.props, answer.id)}>
                                                                <span>Usuń odpowiedź</span>
                                                            </Button>
                                                        </Col>
                                                    )
                                                })
                                            }
                                        </div>

                                        <div className="row" style={{marginTop: '50px', marginBottom: '100px'}}>
                                            <div className="col-sm-6 col-md-6 col-lg-6 cancel_answer_button_content"
                                                 style={{height: '200px'}}>
                                                <CancelAnswerButton className="send_text_cancel_answer_button"
                                                                    points={assignment.pointAmount + assignment.pointBoost}
                                                                    assignmentId={assignment.id}/>
                                                <div className="cancel_answer_button_info">
                                                    <p>Za anulowanie zadania grozi odjęcie punktów z rangi!</p>
                                                </div>
                                            </div>
                                            <div className="col-sm-6 col-md-6 col-lg-6 finish_answer_button_content"
                                                 style={{height: '200px'}}>
                                                <FinishAnswerButton className="send_text_finish_button"
                                                                    points={assignment.pointAmount + assignment.pointBoost}
                                                                    assignmentId={assignment.id}/>
                                                <div className="finish_answer_button_info">
                                                    <p>Upewnij się, że uczeń dostał wszystko, czego potrzebuje!</p>
                                                </div>
                                            </div>
                                        </div>
                                    </form>
                                </Col>
                            </div>
                            <Modal show={this.state.showTimeoutModal}>
                                <Modal.Header>
                                    Hej! Rozwiazujesz dalej to zadanie?
                                </Modal.Header>
                                <Modal.Body>
                                    <h3>Pozostaly czas: {this.state.timeLeft / 1000}</h3>
                                    <Button className='btn btn-primary' onClick={this.handleTimeoutButtonClick}>Tak! Daj
                                        mi jeszcze 5 minut! </Button>
                                </Modal.Body>
                            </Modal>
                        </section>
                        :
                        <Redirect to='/assignments'/>
                )
            })
        )
    }
}

那么如何使计时器仅运行一次?我想念什么?

第一次单击后登录控制台:

300000 AssignmentComponent.js:89
298999 AssignmentComponent.js:89
297997 AssignmentComponent.js:89
296996 AssignmentComponent.js:89
295994 AssignmentComponent.js:89
294992 AssignmentComponent.js:89
293990 AssignmentComponent.js:89
292988 AssignmentComponent.js:89
291986 AssignmentComponent.js:89
290984 AssignmentComponent.js:89
289983 AssignmentComponent.js:89

第二次单击后记录:

300000 AssignmentComponent.js:89
298993 AssignmentComponent.js:89
298999 AssignmentComponent.js:89
297992 AssignmentComponent.js:89
297997 AssignmentComponent.js:89
296990 AssignmentComponent.js:89
296996 AssignmentComponent.js:89
295981 AssignmentComponent.js:89
295993 AssignmentComponent.js:89
294980 AssignmentComponent.js:89
294991 AssignmentComponent.js:89

我还意识到,它不仅运行两次。每次点击都会启动一个新计时器。因此,如果您重新创建组件5次,那么将有5个定时器彼此并排运行。

1 个答案:

答案 0 :(得分:1)

这就是我为我的应用所做的。 在构造函数中添加一个计时器变量,将setTimeout函数分配给要在任何地方启动计时器的计时器变量。并使用该变量清除超时。

class TimerComponent extends Component {
    constructor() {
      super();
      this.timer = null; //Timer initialisation
    }

    startTimeout = () => {
      this.timer = setTimeout(() => {

      }, 500)
    }

    componentDidMount() {
       this.startTimeout();
    }

    componentWillUnmount() {
       clearTimeout(this.timer); //Clearing timeout
       this.timer = null;
    } 
}