React component with pressing Enter

1.4k views Asked by At

I have some react component and needed to work with "Enter" function.

class MyComponent extends Component {
    componentDidMount() {
        console.log('componentDidMount');
        document.removeEventListener('keypress', this.enter);
        document.addEventListener('keypress', this.enter.bind(this));
    }

    componentWillUnmount() {
        console.log('componentWillUnmount');
        document.removeEventListener('keypress', this.enter);
    }

    render() {
        return (...);
    }

    enter(target) {
        if (target.charCode === 13) {
            console.log('fired');
            /* after that component unmounted */
        }
    }
}

Console log show:

componentDidMount
fired
componentWillUnmount

, but when press Enter the console show again fired.

2

There are 2 answers

0
T.J. Crowder On BEST ANSWER

this.enter.bind(this) returns a new function, which is not the same function as this.enter. So your removeEventListener is ignored, because that specific function isn't on the event list.

Remember the result of this.enter.bind(this) and use that when removing.

componentDidMount() {
    console.log('componentDidMount');
    this.boundEnter = this.enter.bind(this);
    document.addEventListener('keypress', this.boundEnter);
}

componentWillUnmount() {
    console.log('componentWillUnmount');
    document.removeEventListener('keypress', this.boundEnter);
}

(There's also no need for your removeEventListener in componentDidMount.)

Since you're using ES2015+ syntax, I'll assume you're transpiling. If you are, you could use an arrow function rather than a method for enter:

class MyComponent extends Component {
    componentDidMount() {
        console.log('componentDidMount');
        document.addEventListener('keypress', this.enter);
    }

    componentWillUnmount() {
        console.log('componentWillUnmount');
        document.removeEventListener('keypress', this.enter);
    }

    render() {
        return (...);
    }

    enter = target => {
        if (target.charCode === 13) {
            console.log('fired');
            /* after that component unmounted */
        }
    };
}

That requires that you enable handling of class properties in your transpiler (in Babel, they're currently part of the stage-2 preset).

2
dfsq On

You never unbind event handler. The problem is that this.enter.bind(this) and this.enter are different functions because Function.prototype.bind creates new "wrapper" function from the original.

Try something like this:

class MyComponent extends Component {
    constructor () {
        this.enter = this.enter.bind(this)
    }

    componentDidMount() {
        console.log('componentDidMount');
        document.removeEventListener('keypress', this.enter);
        document.addEventListener('keypress', this.enter);
    }

    componentWillUnmount() {
        console.log('componentWillUnmount');
        document.removeEventListener('keypress', this.enter);
    }

    render() {
        return (...);
    }

    enter(target) {
        if (target.charCode === 13) {
            console.log('fired');
            /* after that component unmounted */
        }
    }
}

Note, you need to provide the same function to both addEventListener and removeEventListener.