Best practice in React regarding the context `this

2019-05-13 22:44发布

问题:

Which of the following is the best practice with regard to performance in a React class component:

  1. Binding a call back function in the constructor:

    constructor(props)
    {
        /* some code */
        this.onChange= this.onChange.bind(this)
    }
    
    render() {
       return(
          <div>
              <input onChange={this.onChange}
          </div>
       );
    }
    
    onChange(event) {
    /* some code involving 'this' */
    }
    
  2. Using an arrow function instead of a normal function:

    constructor(props)
    {
        /* some code */
    }
    
    render() {
       return(
          <div>
              <input onChange={this.onChange}
          </div>
       );
    }
    
    onChange = (event) => {
    /* some code involving 'this' */
    }
    
  3. Sticking to a normal function but declaring an arrow function in the onChange field:

    constructor(props)
    {
        /* some code */
    }
    
    render() {
    <div>
        <input onChange={(event) => {this.onChange(event)}}
    </div>
    }
    
    onChange(event) {
    /* some code involving 'this' */
    }
    

Thanks!

回答1:

All 3 options, with regards to this, behave the same.

Option 3 is creating a new function on every render and is thus less desired than options 1 and 2 (since such re-creation is unnecessary and could potentially hinder performance)

As far as options 1 and 2, they come down to the same behavior with slight differences unrelated to the behavior of this.

The trick to understanding why they behave the same with this is to know what the following code does:

method = () => {}

It's just syntactic sugar to add a method to an instance:

class A {
  method = () => {}
}

// is the same as

class A {
  constructor() {
    this.method = () => {}
  }
}

See how Babel transpiles it.

Since an arrow function inherits the context it is defined in as this, the context for option 2 is the class itself.

class A {
  constructor() {
    this.method = () => {
      return this;
      //     ^^^^ this points to the instance of A
    }
  }
}

const a = new A();
console.log(a.method() === a); // true

Which is the same thing for option 1:

class A {
  constructor() {
    this.method = this.method.bind(this);
  }
  method() {
    return this;
    //     ^^^^ this points to the instance of A
  }
}

const a = new A();
console.log(a.method() === a); // true

That means your options come down to the difference between:

// option 1
constructor(props) {
  this.onChange = this.onChange.bind(this)
}

onChange() {
 // code for onChange...
}

and

// option 2
constructor(props) {
  this.onChange = () => /* code for onChange */
}

The main advantage I would give to option 1 is that it has a named function instead of an arrow function, which makes debugging a bit easier when examining stack traces (although function name inferences dilutes this point a bit).

The main advantage I would give to option 2 is that it's a bit "cleaner" looking, as in less verbose, code but that is a subjective opinion.

Overall, option 1 and option 2 are practically indifferent.