React Bind

August 29, 2017 by sylvenas

在应用react开发中,大家经常遇到各种this的问题,其中最常见的情况,也是很多新手容易些的代码(在线地址)是:

class App extends React.Component {
    constructor() {
        super();
        this.state = { count: 0 };
    }
    increase() {
        this.setState({
            count: this.state.count + 1
        })
    }
    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick={this.increase}>increaseOne</button>
            </div>
        );
    }
}

上面的代码会报错提示:

Cannot read property 'setState' of null

新手可能在疑惑为何会报错?

原因在于,这里只是把 this.increase、this.decrease 传给 onClick, 真正调用的时候(click event)并没有指定context,从而导致在方法内部的this为null。

React团队在使用ES6开发react的时候中,决定不自动绑定。你可以在找到这篇官方博客中看看他们这样做的原因。

现在我们总结一下如何在JSX中调用ES6 class 中的方法:

1.使用Function.prototype.bind()

由于ES6 class 中的方法都是纯javascript函数,所以集成了Function 原型中的bind方法,所以现在在JSX内部调用increase函数的时候,它内部的this,会指向类的实例,在MDN中有更多的关于bind方法的介绍。

class App extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
  }
  increase() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>{this.state.count}</h1>
        <button onClick={this.increase.bind(this)}>increaseOne</button>
      </div>
    );
  }
}

2.在构造函数中bind this

class App extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
    this.increase = this.increase.bind(this);
  }
  increase() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>{this.state.count}</h1>
        <button onClick={this.increase}>increaseOne</button>
      </div>
    );
  }
}

3.在构造函数中使用箭头函数

ES6箭头函数定义时,会自动保留上下文,我们可以借助这个特性,让 increase 方法以下列方法在构造函数中重新定义

class App extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
    this._increase = () => this.increase();
  }
  increase() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>{this.state.count}</h1>
        <button onClick={this._increase}>increaseOne</button>
      </div>
    );
  }
}

4.使用箭头函数和类属性

我们使用class 属性定义,而不在像方法三种在构造函数中定义类的方法

class App extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
  }
  increase = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>{this.state.count}</h1>
        <button onClick={this.increase}>increaseOne</button>
      </div>
    );
  }
}

5.函数绑定语法

ES7有一个函数绑定语法的提案,详细实现的技术原理,直接查看链接里的源代码,下面只介绍使用方法。

class App extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
    this.increase =::this.increase;
  }
  increase() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>{this.state.count}</h1>
        <button onClick={this.increase}>increaseOne</button>
      </div>
    );
  }
}

6.JSX中直接使用即时绑定

class App extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
  }
  increase() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>{this.state.count}</h1>
        <button onClick={::this.increase}>increaseOne</button>
      </div>
    );
  }
}

参考资料:
[1]: About autobinding in official React blog
[2]: Autobinding, React and ES6 Classes
[3]: Function Bind Syntax in official Babel blog
[4]: Function.prototype.bind()
[5]: Experimental ES7 Class Properties
[6]: Experimental ES7 Function Bind Syntax