2

I am trying to debug create-react-app and when I put a breakpoint on an arrow function I have invalid value inside of this keyword and completely strange behavior of stopping breakpoints (the devtools don't allow to put a breakpoint at a valid js expression, it looks like disabled for a breakpoint. I checked on both FF and Chrome browsers. However, when I change arrow function (()=>{}) to the function(){} declaration, the debugging behavior is correct. Does anyone know what the issue is and what react start up project would you recommend where arrow functions are debugged correctly?

My code in App.js looks like here. Try to put a breakpoint inside of the line:

this.setState({value: this.state.value + 1})

this should be App but it's not the case. It is undefined at this particular breakpoint, although the app's behavior is correct. I suspect something is broken with sourcemaps... What are react projects out there with good setup with webpack that handles sourcemaps correctly?

5
  • 1
    What is this "strange behavior" you're observing? Commented Jul 10, 2018 at 22:41
  • 1
    Could you include the code where you are noticing this strange behavior in the question? Commented Jul 10, 2018 at 22:45
  • My code in App.js looks like here - jsbin.com/zotugemeca/1/edit?js Commented Jul 10, 2018 at 23:08
  • What do you mean that the arrow function behaves strangely? Arrow functions doesn't have their own this, but they have the this of the enclosing lexical scope instead. Commented Jul 10, 2018 at 23:09
  • this should be App but it's not the case. It is undefined at this particular breakpoint, although the app behavior is correct. I suspect something is broken with sourcemaps. Commented Jul 10, 2018 at 23:21

2 Answers 2

3

Without using something like let that = this you can use functions for callbacks in JSX properties in a couple of different ways.

If you want to use it directly as an anonymous function you can use like that:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 0,
    };
  }

  render() {
    return (
        <button onClick={(function () {
          this.setState(prevState => ({ value: prevState.value + 1 }));
        }).bind(this)}>
        Click me
        </button>
    );
  }
}

You are binding the function to this here directly. I haven't seen any example of that. Generally people use something like this:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 0,
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({ value: prevState.value + 1 }));
  }

  render() {
    return (
        <button onClick={this.handleClick}>
        Click me
        </button>
    );
  }
}

Here we are using our callback function as a reference and binding it in out constructor.

Another alternative is using an arrow function. For this situation you don't need to bind your function. Also, we are using class-fields here, hence no need to use constructor at all.

class App extends React.Component {
  state = { value: 0 };
  handleClick = () => {
    this.setState(prevState => ({ value: prevState.value + 1 }));
  }

  render() {
    return (
        <button onClick={this.handleClick}>
        Click me
        </button>
    );
  }
}

In a callback for JSX props this' scope changes, it does not refer to the class anymore. So either you will bind it to this or use an arrow function which does not change the scope of this.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much! It was very helpful!!
2

Sometimes debug tools can struggle to correctly place breakpoints for lambda functions in these cases. Perhaps you could achieve the same effect by temporarily adding debugger to your source code as follows, to force the breakpoint to hit:

import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {

    constructor(props) {
        super(props);
        this.state = {
            value: 0,
        };
    }

    render() {

        return (
            <div className="App">
                <header className="App-header" onClick={() => {

                    debugger; // ADD THIS: It will act as a breakpoint and force the developer-tools to break so you can step through the code

                    this.setState({value: this.state.value + 1})
                }}>
                    <img src={logo} className="App-logo" alt="logo"/>
                    <h1 className="App-title">Welcome to React, the state is {this.state.value}</h1>
                </header>
                <p className="App-intro">
                To get started, edit <code>src/App.js</code> and save to reload.
                </p>
        </div>);
    }
}

export default App;

1 Comment

Thank you for the suggestion, I added the debugger but this is still undefined while debugging.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.