Chrome, Firefox debuggers not displaying the correct value for 'this' in a react app

Here's a bit of code, within a react component class (scaffolded using CRA 2)

  click = () => {
    console.log(this, "hello");
    let x = 1 + 1; //This is just here to let chrome put a break point here. 
  }

When this code runs, it will print the component object to the console.

However - if I attach a debugger to that point, both Chrome (68), and Firefox (63) will show 'this' as undefined.

What's going on here?

Is it something to do with the transform-class-properties babel plugin being used to create click as a class property?

Edit: Yes, that seems like exactly what it is.

If we manually bind the method like:

  constructor() {
    super();
    this.click2 = this.click2.bind(this);
  }

  click2() {
    console.log(this, "hello");
    let x = 1 + 1;
  }

then it works fine.

In any case - is there a convenient way to solve this, so I don't have to put all those bind statements in?

Answers:

Answer

I created an example on CodeSandbox that I think reproduces your issue, though I'm not sure. Please create your own example if it does not. The relevant code is included below.

In this example, the code works fine. console.log(this, "hello") logs a Square object + "hello" as you might expect. If you put a breakpoint on the let y = 2 + 2 line, the Chrome debugger will show

this: undefined
x: 2
y: undefined

Of course, y is undefined because the let y statement has not executed yet. x is defined, as expected. this is undefined because React and Babel are jumping through lots of hoops under the covers, and this is, in fact, undefined. If you want to access this from the debugger, you need to use _this. In fact, even though you put a breakpoint on the line let y = 2 + 2, that is not the actual source being executed or where the actual breakpoint is. What you are seeing is a convenience provided by a source map that lets you view and set a breakpoint on the code you wrote despite the fact that the actual code being run is completely different (the result of processing by Babel etc.).

The code I wrote is:

class Square extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: null
    };
  }

  click = () => {
    console.log(this, "hello");
    let x = 1 + 1; //This is just here to let chrome put a break point here.
    let y = 2 + 2; //This is just here to let chrome put a break point here.
  };

  render() {
    return (
      <button className="square" onClick={this.click}>
        {this.props.value}
      </button>
    );
  }
}

the code actually running is:

var Square =
/*#__PURE__*/
function (_React$Component) {
  (0, _inherits2.default)(Square, _React$Component);

  function Square(props) {
    var _this;

    (0, _classCallCheck2.default)(this, Square);
    _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(Square).call(this, props));
    (0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "click", function () {
      console.log((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "hello");
      var x = 1 + 1; //This is just here to let chrome put a break point here.

      var y = 2 + 2; //This is just here to let chrome put a break point here.
    });
    _this.state = {
      value: null
    };
    return _this;
  }

  (0, _createClass2.default)(Square, [{
    key: "render",
    value: function render() {
      return _react.default.createElement("button", {
        className: "square",
        onClick: this.click
      }, this.props.value);
    }
  }]);
  return Square;
}(_react.default.Component);

Because of the React.js internals (in particular, the way it wraps events), by the time the handler is called, this is undefined. If you look at the call stack, you see that executeDispatch calls invokeGuardedCallbackAndCatchFirstError with an explicit value of undefined for the context object that is ultimately the value of this inside the callback. React and Babel try to hide all this from you when you are writing code, but they cannot completely hide this from the debugger, particularly with respect to this, so in this case you have to go to the actual code to see that you need to refer to _this in the debugger.

Answer

I think you need to set babel options to disable module processing. See this answer:

How to stop babel from transpiling 'this' to 'undefined'

In your .babelrc:

{
  "presets": [
    [ "es2015", { "modules": false } ]
  ]
}
Answer

An pragmatic alternative - if what you're wanting to do is inspect the state/props of a react component at a certain point, is to enter the break point as per normal - but instead of using the debugger to inspect the state - use the react dev tools plugin to actually examine the state.

This might prove to be a bit fiddly, but it's an option.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.