2

Have the following simpliest component.

class App extends Component {
  constructor(props) {
    super(props);
    console.log(this);
    this.state = {
      name: "John"
    }
  }

  clickHandler() {
    this.setState({
      name: "Mark"
    })
  };

  render() {
    return (
      <div className="App">
        {this.state.name}
        <button onClick={this.clickHandler.bind(this)}></button>
      </div>
    )
  };
}

export default App;

If I write that

<button onClick={this.clickHandler}></button>

it says that this in

this.setState({
      name: "Mark"
    })

is undefined. That row

<button onClick={console.log(this)}></button>

shows that this is App. So if this is App why clickHandler does not bind automatically to that object before the dot?

<button onClick={this.clickHandler}></button>

As far as I understand in "context.function" the context should be bound to the function. Or it only works for "context.function()"?

Anthony
  • 73
  • 5
  • You need to compare `onClick={this.clickHandler}` with `onClick={function(e){console.log(this)}}` – Bergi Feb 19 '22 at 17:17

1 Answers1

0

Or it only works for "context.function()"?

That's exactly right. It depends on where the function is invoked.

Doing

<button onClick={this.clickHandler}></button>

passes the clickHandler as a parameter. React's internals see something not completely dissimilar to:

function button({ onClick }) {
  // ...
  theActualDOMButtonElement.addEventListener('click', onClick);
}

or

function button({ onClick }) {
  // ...
  theActualDOMButtonElement.addEventListener('click', (e) => {
    const reactCreatedSyntheticEvent = retrieveSyntheticEvent(e);
    onClick(reactCreatedSyntheticEvent);
  });
}

You're passing the this.clickHandler as a parameter, not invoking it immediately - since it gets invoked later, and how it's invoked later determines the this, you can't just do onClick={this.clickHandler} without making sure the this is correct, such as invoking the function inline, or by using a class field, or by using a functional component.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • So you mean that how it's invoked later determines the this. What is the process of that invoking? And why this is later being determined to undefined? – Anthony Feb 19 '22 at 16:57
  • If the function isn't bound or an arrow function, then: if it's called like `someObj.someFn()`, its `this` will be `someObj`. If it's called like `someFn` - not as part of an object - it's `this` will be `undefined` or the global object, depending on whether you're in strict mode or not. – CertainPerformance Feb 19 '22 at 16:59
  • So someObj.someFn (not someObj.someFn()) is just a parameter that CANNOT be used without binding method. That means we only take the someFn from SomeObj and the fact that someObj is before dot doesn't have any impact in this case. – Anthony Feb 19 '22 at 17:08
  • Yes - if it isn't invoked in the same expression, the fact that it's part of an object makes no difference. `onClick={someObj.clickHandler}` is just like `const handler = someObj.clickHandler; ... onClick={handler}` – CertainPerformance Feb 19 '22 at 17:11
  • Last question is about arrow functions. Why () => clickHandler() binds context? – Anthony Feb 19 '22 at 18:03
  • It's not that it binds, it's that it gets invoked at a place where you can control it - when you write the `()` in your own code, you can control the calling context. – CertainPerformance Feb 19 '22 at 18:04
  • this.clickHandler()}> The calling context is the button? – Anthony Feb 19 '22 at 18:14
  • The calling context is what comes before the `.` (or `[]`). `obj.fn()` -> `obj` is the calling context. `fn()` -> no calling context. – CertainPerformance Feb 19 '22 at 18:21
  • Thank you. () => syntax plays the invocation control role. Understood. – Anthony Feb 19 '22 at 18:25