Search code examples
javascriptreactjssetstate

"this.setState is not a function" when using setState inside a nested addEventListener function


I for the life of me can't get passed this TypeError inside my react component. I've read every relevant thread and implemented almost every suggested fix to no avail.

import React, { Component } from "react";

class Wave extends Component {
    state = {
        position: 44
    };

    changePosition = () => {
        let parentDiv = document.getElementById('parentDiv');
        //this.setState({position: 3})

        parentDiv.addEventListener('click', function (e) {
            let offset = this.getClientRects()[0];          
            alert(e.clientX - offset.left);
            let xcord = e.clientX - offset.left;
            this.setState({position: xcord})
        }, false);
    }

    render() {          
        return (
            <div id={"parentDiv"}>
                <div id={"childDiv"} onClick={this.changePosition}>HI THERE!</div>
            </div>
        );
    }
}

export default Wave;

I understand that I'm losing the React class this context and have tried binding the changePosition method inside the component constructor which didn't seem to work. I also tried using .bind(this) at the end of the addEventListener function -

changePosition = () => {
    let parentDiv = document.getElementById('parentDiv');
    //this.setState({position: 3})

    parentDiv.addEventListener('click', function (e) {
        let offset = this.getClientRects()[0];          
        alert(e.clientX - offset.left);
        let xcord = e.clientX - offset.left;
        this.setState({position: xcord})
    }.bind(this), false);
}

But when I do this, as well as when I try changing the addEventListener function to an arrow function (e) => { then I'm left with TypeError: this.getClientRects is not a function

Any enlightenment on how to get around this error is extremely appreciated as this has now eaten up the last 2-3 hours of my day. Thank you very much!


Solution

  • parentDiv.addEventListener('click', function (e) {
        let offset = this.getClientRects()[0];  // this refers to parentDiv        
    }
    

    Change this to an arrow function to correctly bind this

    parentDiv.addEventListener('click', (e) => {
        let offset = parentDiv.getClientRects()[0];  // this refers to Wave component, so we use the actual div reference
        alert(e.clientX - offset.left);
        let xcord = e.clientX - offset.left;
        this.setState({position: xcord}); // works fine
    }, false);