How calling a callback directly fixes the 'this' in a React Class Component?

I would like to learn how is the value of 'this' set when a function called in JSX as a callback to an eventHandler. I noticed that when I call it directly there is no issue accessing state without getting the famous undefined 'this' error, like so:

import React from "react";

class Accordion extends React.Component {
  state = { term: "random term" };

  onTitleClick() {
    console.log("Title is clicked");
    console.log(this.state.term);
  }

  render() {
    const renderedItems = this.props.items.map((item) => {
      return (
        <React.Fragment key={item.title}>
          <div className="title active" onClick={this.onTitleClick()}>
            <i className="dropdown icon"></i>
            {item.title}
          </div>
          <div className="content active">
            <p>{item.content}</p>
          </div>
        </React.Fragment>
      );
    });
    return <div className="ui styled accordion">{renderedItems}</div>;
  }
}

export default Accordion;


When you pass it as just a reference, the famous 'this' is undefined error comes back. Then we know how to bind the 'this' and so on. I feel like I just memorized the solution and now would like to learn the difference.

2 answers

  • answered 2020-09-25 13:39 szczocik

    When you pass it as a reference, the this is undefined. In order for this to work, you need to bind the function to the class. You can do that in the constructor

    constructor(props) {
        this.state = { term: "random term" };
        this.onTitleClicked = this.onTitleClicked.bind(this);
    }
    

    Also, when you pass it to the component, don't call the function, just pass it

    <div className="title active" onClick={this.onTitleClick}>
    

    Notice missing parenthesis by the this.onTitleClick call.

  • answered 2020-09-25 13:40 Yousaf

    onClick={this.onTitleClick()} - This is not how you set the event listener. You just need to pass the name of the function instead of calling it yourself.

    As far as your question regarding the value of this is concerned, value is set depending on how the function is called. This is not specific to React, this is just how value of this is set in Javascript.

    I noticed that when I call it directly there is no issue accessing state without getting the famous undefined 'this' error

    That's because when you call it like this: this.onTitleClick() - onTitleClick() is called on this which refers to the Accordion component. But as mentioned at the start of this answer, this is not how you set the event listener. Instead of calling this method yourself, you need to let javasctipt call it.

    When you pass it as just a reference, the famous 'this' is undefined error comes back

    This is the correct way to add an event listener but you get an error because when javascript calls the event handler function, value of this is not your component, i.e. Accordion.

    In case of React, this inside the event handler function is null but using vanilla javascript, this inside an event handler is the HTML element on which the event listener was added. Keep in mind that this applies when you use the regular function as a event handler function.

    To solve this issue, you have two options:

    • Explicitly set this using .bind()

      this.onTitleClick = this.onTitleClick.bind(this);    
      
    • Use arrow functions instead of regular functions as event handlers

      onTitleClick = () => {
         console.log("Title is clicked");
         console.log(this.state.term);
      }
      

    Following are couple of related questions that might help in understanding this further: