如何在反应中正确更新状态

时间:2018-10-05 22:04:33

标签: javascript reactjs

我目前正在使用React开发一个简单的秒表应用程序,并且在单击“开始”按钮时更新“ secondsPassed”道具时遇到一些问题。我目前收到错误消息:

TypeError:无法设置未定义的属性“计数器”

在handleStart事件中发生错误。我将thisStart定义为具有handleStart事件的范围。我相信我在构造函数中定义了变量计数器,并且尝试通过将传递的秒数增加1来更新该计数器。

import React, { Component } from 'react';
import '../assests/stopwatch.css';

class StopWatch extends Component {
    // handle state
    constructor(props) {
        super(props);

        this.state = {
            secondsPassed: 0,
            counter: 0
        };

    }

    // define event functions for start, pause and reset

    handleStart() {
        let thisStart = this;
        this.counter = setInterval(() => {
            thisStart.setState({
                secondsPassed: (thisStart.state.secondsPassed + 1)
            });
        }, 1000)
    }

    handlePause() {
        clearInterval(this.counter);
    }

    handleReset() {
        this.getInitialState()
    }


    // define function that shows the seconds passed
    // the initial state is going to be zero
    getInitialState() {
        return { secondsPassed: 0 };
    }

    getSeconds() {
        return ('0' + this.state.secondsPassed % 60).slice(-2);
    }

    getMinutes() {
        return Math.floor(this.state.secondsPassed / 60);
    }

    render() {
        return (
            <React.Fragment>

                <div id="container">

                    <h1>Stopwatch APP</h1>

                    <div id="stopwatch-container">

                        <div className="time-container">
                            <span>{this.getMinutes()}:{this.getSeconds()}</span>
                        </div>

                        <div className="button-container">
                            <div className="start-button">
                                <button type="button" onClick={this.handleStart}>Start</button>
                            </div>

                            <div className="pause-button">
                                <button type="button" onClick={this.handlePause}>Pause</button>
                            </div>

                            <div className="reset-button">
                                <button type="button" onClick={this.handleReset}>Reset</button>
                            </div>
                        </div>
                    </div>
                </div>

            </React.Fragment >
        );
    }
}

export default StopWatch;

3 个答案:

答案 0 :(得分:1)

¿计数器是变量还是函数?他正在寻找一个名为 counter 的全局属性。 分配状态的正确方法是:

this.setState( obj );

其中 obj 是您的对象。我不明白为什么您要使用'this.state.secondsPassed',但是使用计数器'this.counter'时它应该是相同的。

阅读这两篇关于React StatesLifecycle(还包含计时器功能)的文章,以便您首先可以阐明当前的理解。

编辑: 也许您正在尝试使用counter和secondsPassed重用它们的值,在这种情况下,您可以使用发送给setState的参数:

this.setState( (previousState) => {
   previousState.counter = 0;
   return previousState;
}

在这里,我们只是将先前的 secondsPassed 值与 counter 的新值一起使用。

答案 1 :(得分:1)

您应该绑定回调。 要么在JSX中使用

<button ... onClick={this.handleStart.bind(this)}...>

或在构造函数中

constructor () {
  super(props);
  this.handleStart = this.handleStart.bind(this);
  // do this for the rest of the functions
}

您无需为interval函数创建自定义范围,简单的箭头函数即可解决问题。

答案 2 :(得分:1)

我假设您在handleStart中定义了this.counter,因为您想将其用作clearInterval的ID,但是我认为您应该使用this.state.counter而不是this.counter,并且您没有在构造函数中绑定该函数,这会导致一些问题。

constructor(props) {
        super(props);
        this.handleStart = this.handleStart.bind(this)
        this.handlePause = this.handlePause.bind(this)
        this.getInitialState = this.getInitialState.bind(this)
        this.handleReset = this.handleReset.bind(this)
        this.state = {
            secondsPassed: 0,
            counter: 0
        };

    }
...
 handleStart() {
            this.state.counter = setInterval(() => {
                this.setState({
                    secondsPassed: this.state.secondsPassed + 1
                })
            },1000)
        }
    handlePause() {
            clearInterval(this.state.counter);
        }

顺便说一句,我认为getInitialState中的返回没有意义,应该使用setState

getInitialState() {
        // return { secondsPassed: 0 };
        this.setState({
            secondsPassed: 0
        })
    }