4

I am using an arrow function to bind this inside a React component. Please see the handleChange() function below. When I put a breakpoint inside the arrow function, I find something very strange: this is defined, but this.props is undefined. In spite of all this, this.props.onChange() is properly invoked!!! Is there an explanation for this strange behavior?

class MyComponent extends React.Component {
    render() {
        const { someProp } = this.props;

        // <TextField> is an input component from Material UI library
        return (
            <TextField
                onChange={this.handleChange}
            />
        );
    }

    handleChange = event => {
        const value = event.target.value;
        this.props.onChange(value);
    };
}

P.S. On the other hand, the render() method behaves normally - this.props is defined.

Update

Here's the transpiled code produced by Babel:

_this.handleChange = function(event) {
    var value = event.target.value;
    _this.props.onChange(value);
}
nem035
  • 31,501
  • 5
  • 73
  • 88
Naresh
  • 18,757
  • 25
  • 99
  • 162

1 Answers1

2

More likely than not, you're being tricked by babel.

Since you're using a babel-preset < 2017, the transpilled code for:

class A {
  method = () => {
    this.prop.a();
  };
}

would look something like:

var A = function A() {
  var _this = this;

  this.method = function () {
    _this.prop.a();
  };
};

Looking at the code above, both _this and this within method are seemingly pointing to the class instance (because this in a function called as a method of an object binds to that object).

However, in JS this is a dynamic property so we cannot statically determine its value in a predictable fashion just by reading the code; we have to run it.

This means that the this inside handleChange isn't necessarily the same this you're expecting. It depends on how handleChange is called. However, no matter how we call handleChange, _this won't be affected (hence one reason why Babel does this)

In your particular code, you are passing handleChange to the onChange event handler for TextField, which will override this to be undefined by default.

React event handlers override the context to undefined (by calling the handler as a plain function):

ReactDOM.render(
  <input onChange={logThis} placeholder="Start typing" />,
  document.querySelector('#example')
)

function logThis() {
  console.log(this);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="example"></div>

This means that in handleChange, this is bound by whatever TextField sets it to while _this still points to the instance of MyComponent.

Hence everything still works (because _this is correct), but this is most likely undefined.

nem035
  • 31,501
  • 5
  • 73
  • 88