React: Dealing with 'This' and Event Handlers | Perficient Digital

React: Dealing with ‘This’ and Event Handlers

React is a JavaScript library for building user interfaces (UIs). One of the important aspects of building UIs is that you must be able to react (get it?) to user interaction. React has its way to handle events, but it’s up to the developers to handle the value of the this keyword inside the handle event functions if we need to access, for example, a state property inside of it.

The value of this in JavaScript is a common gotcha for developers new to the language, especially for those with experience in languages that have a more traditional way of binding this. The most important thing to remember is that this changes depending on how or where a function is called. When dealing with event handlers, the caller of the function ends up being the JSX element the handle function is attached to, thus being the value of this. However, most of the time (if not all the time), we want this to be the React component class/function, not the JSX element.

Let’s look at some different ways to deal with this problem.

First let’s look at a bad example. We have a controlled input on a SearchBar component and we want to propagate its value up. We received an onSubmit function on the props of the component and we need to call it when the form is submitted.

class SearchBar extends React.Component {
    state = { term: '' };

    onFormSubmit(e) {
        e.preventDefault();

        this.props.onSubmit(this.state.term);
    };

    render() {
        return (
            <div className="ui segment">
                <form onSubmit={this.onFormSubmit} className="ui form">
                    <div className="field">
                        <label>Image Search</label>
                        <input
                            type="text"
                            value={this.state.term}
                            onChange={e => this.setState({ term: e.target.value })}
                        />
                    </div>
                </form>
            </div>
        );
    }
}

This will fail because this.props and this.state inside onFormSubmit will be undefined, because this would be the form element, not the SearchBar component.

A simple fix for this would be to use the ES5 bind function, which lets us bind a function’s this value to the provided value.

<form onSubmit={this.onFormSubmit.bind(this)} className="ui form">

This might work on this example, but what happens if you need to use the same event handler on several elements or components?

<MyInput onChange={this.handleChange.bind(this)} />
<MyAnotherInput onChange={this.handleChange.bind(this)} />

That doesn’t look that good. However, thanks to the ES6 class notation, we can use the constructor function to bind our methods. The previous example would then look like this:

class SearchBar extends React.Component {
    constructor(props) {
        super(props);

        this.state = { term: '' };

        this.onFormSubmit = this.onFormSubmit.bind(this);
    }

    onFormSubmit(e) {
        e.preventDefault();

        this.props.onSubmit(this.state.term);
    }

    render() {
        return (
            <div className="ui segment">
                <form onSubmit={this.onFormSubmit} className="ui form">
                    <div className="field">
                        <label>Image Search</label>
                        <input
                            type="text"
                            value={this.state.term}
                            onChange={e => this.setState({ term: e.target.value })}
                        />
                    </div>
                </form>
            </div>
        );
    }
}

This works exactly the same, but we do the binding only once on the constructor. By doing that, we can reuse the same event handler on several properties in the JSX.

But there’s an even better and cleaner way to do this, and that’s using the ES2015 arrow functions.

class SearchBar extends React.Component {
    state = { term: '' };

    onFormSubmit = e => {
        e.preventDefault();

        this.props.onSubmit(this.state.term);
    };

    render() {
        return (
            <div className="ui segment">
                <form onSubmit={this.onFormSubmit} className="ui form">
                    <div className="field">
                        <label>Image Search</label>
                        <input
                            type="text"
                            value={this.state.term}
                            onChange={e => this.setState({ term: e.target.value })}
                        />
                    </div>
                </form>
            </div>
        );
    }
}

The arrow function automatically binds the this value to the enclosing lexical context, which is where the function is defined (in this case, the SearchBar class). Using this method, we don’t need the constructor at all.

If you’ve been observant, you might have noticed that, in the previous examples, the onChange handler of the input element has been using an arrow function all along! This is generally considered a bad practice because React needs to create a new instance of that function each time the render method is triggered.

So the final version of the example would be:

class SearchBar extends React.Component {
    state = { term: '' };

    onFormSubmit = e => {
        e.preventDefault();

        this.props.onSubmit(this.state.term);
    };

    onInputChange = e => this.setState({ term: e.target.value });

    render() {
        return (
            <div className="ui segment">
                <form onSubmit={this.onFormSubmit} className="ui form">
                    <div className="field">
                        <label>Image Search</label>
                        <input type="text" value={this.state.term} onChange={this.onInputChange} />
                    </div>
                </form>
            </div>
        );
    }
}

That looks cleaner and we avoid unnecessary performance issues by not declaring functions on JSX properties.

Both methods, that is using bindings or arrow functions, are acceptable ways to deal with the this keyword value. In my opinion, arrow functions look much cleaner and are easier to write as well. However, in order to use them, you will need to have Babel properly configured to use the proposed class property feature, since there’s still not full support for this across all browsers.

I hope this has been a helpful look into handling the value of the this keyword in React’s event handler functions. If you have any suggestions for future topics or if you want to share your experiences using React, leave a comment below.

Leave a Reply