1

I have implemented a vanilla js countdown into a react component as follow:

import React, { Component } from 'react';

class CustomCountDown extends Component {
    constructor(props) {
        super(props);

        this.endTime;
        this.msLeft;
        this.time;
        this.hours;
        this.mins;
        this.element;
    }

    twoDigits( n ){
        return (n <= 9 ? "0" + n : n);
    }

    updateTimer() {
        this.msLeft = this.endTime - (+new Date);
        if (this.msLeft < 1000 ) {
            element.innerHTML = "countdown's over!";
        } else {
            this.time = new Date(this.msLeft );
            this.hours = this.time.getUTCHours();
            this.mins = this.time.getUTCMinutes();
            this.element.innerHTML = (this.hours ? this.hours + ':' + this.twoDigits( this.mins ) : this.mins) + ':' + this.twoDigits( this.time.getUTCSeconds() );
            setTimeout( this.updateTimer, this.time.getUTCMilliseconds() + 500 );
        }
    }

    countdown( elementName, minutes, seconds ) {
        this.element = document.getElementById( elementName );
        this.endTime = (+new Date) + 1000 * (60*minutes + seconds) + 500;
        this.updateTimer();
    }

    componentDidMount() {
        this.countdown("count", 1, 30);
    }

    render() {
        return(
            <div id="count">
            </div>
        );
    }
}

export default CustomCountDown;

I can't figure out why I am getting the following error:

enter image description here

2
  • 3
    you need to bind this (react component context) with updateTimer function, put this line in the constructor: this.updateTimer = this.updateTimer.bind(this) Commented Aug 25, 2017 at 9:16
  • None of your functions have a proper binding. You need to bind each of the fuctions where you are refering the this variable belonging to the React class context Commented Aug 25, 2017 at 9:21

1 Answer 1

3

When you pass this.updateTimer to setTimeout you loose context, i.e. this no longer points to your component instance. You need to keep the context either way:

setTimeout( this.updateTimer.bind(this), this.time.getUTCMilliseconds() + 500 );
setTimeout( () => this.updateTimer(), this.time.getUTCMilliseconds() + 500 );

As a better alternative, you can bind updateTimer in the constructor. This won't create new function every time updateTimer is called:

constructor(props) {
    // ...

    this.updateTimer = this.updateTimer.bind(this);
}
Sign up to request clarification or add additional context in comments.

5 Comments

I believe that also creating component method like updateTimer = () => {} would do the job
@Kejt it would but unlike the solutions above, it will require a transpiler
indeed :) but for supporting fully arrow functions, there should be a transpiler as well, right?
@Kejt not necessary, all major browsers support it for some time now: kangax.github.io/compat-table/es6
the key word here is fully :D but I agree with you :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.