Chrome, Firefox debuggers not displaying the corre

2020-03-05 06:42发布

问题:

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?

回答1:

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.



回答2:

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 } ]
  ]
}


回答3:

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.