AfternoonTiger
AfternoonTiger

Reputation: 397

how to properly update state within react

I'm currently working on a simple stopwatch application using react, and I'm have some issues updating the secondsPassed prop when the start button is clicked. I'm currently getting the error:

TypeError: Cannot set property 'counter' of undefined

The error is occurring in the handleStart event. I defined thisStart to have the scope of the handleStart event. I believe I defined the variable counter in the constructor and I'm trying to update that counter by incrementing the seconds passed by 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;

Upvotes: 0

Views: 482

Answers (3)

Root
Root

Reputation: 2361

I assume you define this.counter in handleStart because you want to use it as a id for clearInterval ,but I think you should this.state.counter instead of this.counter,and you didn't bind the function in constructor ,it will lead to some problems.

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);
        }

BTW, I think the return in getInitialState make no sense,it should use setState

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

Upvotes: 1

Jose Cordero
Jose Cordero

Reputation: 526

¿counter will be a variable or a function? He's looking for a global property called counter. The correct way to assign states is:

this.setState( obj );

Where obj is your object. I don't understand why you use 'this.state.secondsPassed' but with counter just 'this.counter' when it'll should be the same.

Read these two articles talking about React States and Lifecycle (also contains a timer function) so you can first clarify your current understanding.

EDIT: Perhaps you're trying to use counter and secondsPassed re-using their values, for that case you could use the parameter sent to setState:

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

Here, we're just using the previous value of secondsPassed with a new one for counter.

Upvotes: 1

eavichay
eavichay

Reputation: 517

you should bind the callbacks. either in the JSX use

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

or in the constructor

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

You don't need to create a custom scope for the interval function, simple arrow function will do the trick.

Upvotes: 1

Related Questions