Tolsee
Tolsee

Reputation: 1705

React class component unusual this binding on methods

For an example class component like below:

class Todo extends Component {
  state = {
    list: ["First Todo"],
    text: ""
  };

  handleSubmit(e) {
    e.preventDefault();
    if (this && this.setState) {
      console.log("this present in handleSubmit");
      this.setState(prevState => ({
        list: prevState.list.concat(this.state.text),
        text: ""
      }));
    } else {
      console.log("this not present in handleSubmit");
    }
  }

  handleChange(e) {
    if (this && this.setState) {
      console.log("this present in handleChange");
      this.setState({
        text: e.target.value
      });
    } else {
      console.log("this not present in handleChange");
    }
  }

  removeItem(index) {
    if (!this || !this.setState) {
      console.log("this not present in removeItem");
    }
    console.log("this present in removeItem");
    const list = this.state.list;
    list.splice(index, 1);
    this.setState({ list });
  }

  render() {
    return (
      <div>
        <h1>TODO LIST</h1>
        <form onSubmit={this.handleSubmit}>
          <input value={this.state.text} onChange={e => this.handleChange(e)} />
          <button>Add</button>
          <ol>
            {this.state.list.map((item, index) => {
              return (
                <li key={index}>
                  {item}
                  <button onClick={() => this.removeItem(index)}>Delete</button>
                </li>
              );
            })}
          </ol>
        </form>
      </div>
    );
  }
}

The behavior of this binding to the class methods is not consistent.

Playing around with the component we will find that, handleChange and removeItem has the correct this context, whereas handleSubmit has this context as undefined.

Both of the function which has correct this context is represented as an arrow function in jsx. Like below:

<input value={this.state.text} onChange={e => this.handleChange(e)} />

While the handleSubmit is passed as function itself. Like below:

<form onSubmit={this.handleSubmit}>

But, I really don't know why this is happening. Because, In my understanding, it should not have mattered how the function was passed i.e. as the function itself or arrow representation as above.

Edit React example

Upvotes: 1

Views: 41

Answers (2)

Vencovsky
Vencovsky

Reputation: 31625

In my understanding, it should not have mattered how the function was passed...

So here is new thing to learn

Passing onChange={e => this.handleChange(e)} will be the same as using .bind in the constructor or passing the reference and using .bind.

When passing it as an arrow function in the render method, it will get the this of the component instead of the method's this.

You should notice that onChange={e => this.handleChange(e)} is not a good practice because on every render you will be creating a new function.

Upvotes: 1

Dupocas
Dupocas

Reputation: 21317

Arrow functions have lexical this. Which means its value is determined by the surrounding scope. So when you use it instead of class methods the this value will be maped to the instance. But when you call this.onSubmit this will be refering to the local scope and not to the instance itself. To solve it either use arrow functions or bind the onSubmit method in your constructor.

constructor(props){
   super(props)
   this.onSubmit = this.onSubmit.bind(this)
}

Upvotes: 4

Related Questions