React.js ES6避免将'this'绑定到每个方法

时间:2015-08-24 22:20:59

标签: javascript reactjs ecmascript-6 es2015

最近,我开始修补React.js,我喜欢它。我从常规ES5开始,为了掌握一切,文档全部用ES5编写......

但是现在我想尝试ES6,因为它有光泽和新颖,它似乎简化了一些事情。令我困扰的是,对于我添加到组件类中的每个方法,我现在必须绑定'this',否则它不起作用。所以我的构造函数最终看起来像这样:

constructor(props) {
  super(props);
  this.state = { ...some initial state... }

  this.someHandler = this.someHandler.bind(this);
  this.someHandler = this.someHandler.bind(this);
  this.someHandler = this.someHandler.bind(this);
  this.someHandler = this.someHandler.bind(this);
  this.someHandler = this.someHandler.bind(this);
  this.someHandler = this.someHandler.bind(this);
  this.someHandler = this.someHandler.bind(this);
}

如果我要为我的班级添加更多方法,这将变成一个更大,更丑陋的混乱。

我的问题是,有没有办法解决这个问题,或者至少让它变得更容易,更短,更难看?我想尝试与ES6一起使用React的主要原因之一是让我的代码更简洁,但这恰恰相反。任何建议或意见将不胜感激。

9 个答案:

答案 0 :(得分:52)

您可以使用class fields在构造函数外部进行绑定。它们如下所示:

class Foo extends React.Component {

  handleBar = () => {
    console.log('neat');
  };

  handleFoo = () => {
    console.log('cool');
  };

  render() {
    return (
      <div
        onClick={this.handleBar}
        onMouseOver={this.handleFoo}
      />
    );
  }

}

Babel通过其class properties transform实验性地支持类字段,但它们仍然是“实验性的”,因为它们是Stage 3 Draft(尚未在Babel预设中)。

然而,您需要手动执行绑定,直到ES7或在Babel中启用该功能。 Babel在React on ES6+上的博客文章中简要介绍了这个主题。

答案 1 :(得分:11)

另一种选择是使用装饰器。您在原型上声明了一个getter,并且在第一次访问实例时,它定义了一个具有该函数的绑定版本的自己的属性。

但是有一个问题!在开发中它不会取代属性,它将在每次访问时绑定。 这意味着您不会破坏react-hot-loader 。至少对我而言,这非常重要。

我创建了一个提供此功能的库class-bind

import {bound} from 'class-bind';

class App {
  constructor(){
    this.foo = 'bar';
  }

  @bound
  returnsFoo(){
    return this.foo;
  }

  render(){
    var returnsFoo = this.returnsFoo;
    return (
      <div>
        {returnsFoo()} === 'bar'
      </div>
    );
  }
}

装饰师对你来说太不稳定了?你可以使用相同的好处绑定所有东西或某些东西。

import {bind, bindAll} from 'class-bind';

bind(App.prototype, 'returnsFoo');

// or
bindAll(App.prototype);

答案 2 :(得分:1)

Ssorallen的建议很棒,但如果你想要另一种方式:

    class AppCtrlRender extends Component {
        binder(...methods) { methods.forEach( (method) => this[method] = this[method].bind(this) ); }

        render() {
            var isMobile = this.state.appData.isMobile;
            var messages = this.state.appData.messages;
            return (
                <div id='AppCtrlSty' style={AppCtrlSty}>
                    React 1.3 Slider
                    <br/><br/>
                    <div className='FlexBoxWrap'>
                        <Slider isMobile={isMobile}/>
                        <JList data={messages}/>
                    </div>
                </div>
            );
        }
    }

    var getAppState = function() {
        return {
            appData: AppStore.getAppData()
        };
    };

    export default class AppCtrl extends AppCtrlRender {
        constructor() {
            super();
            this.state = getAppState();
            this.binder('appStoreDidChange');
        }

        componentDidMount() {
            var navPlatform = window.navigator.platform;
            Actions.setWindowDefaults(navPlatform);
        }
        componentWillMount() { AppStore.onAny(this.appStoreDidChange); }
        componentWillUnmount() { AppStore.offAny(this.appStoreDidChange); }
        appStoreDidChange() { this.setState(getAppState()); }
    }

您可以向this.binder添加任意数量的方法(&#39;方法1&#39;,&#39;方法2&#39;,...)

答案 3 :(得分:1)

如果使用stage-0,则存在函数绑定语法。

class MyComp extends Component {

  handleClick() { console.log('doing things') }

  render() {
    return <button onClick={::this.handleClick}>Do Things</button>
  }

}

这导致this.handleClick.call(this)的结构,我认为它通常足够高效。

答案 4 :(得分:1)

避免绑定的一个想法

class MyComp extends Component {

  render() {
    return <button onClick={e => this.handleClick(e)}>Do Things</button>
  }

}

免责声明:未经测试,也不能轻易处理多个参数(在这种情况下,有一个,事件(e)。

此外,这个答案可能是要做的事情的一个例子,根据这篇文章可能值得一读:

https://daveceddia.com/avoid-bind-when-passing-props/

答案 5 :(得分:0)

我实际上更喜欢通过传递子上下文来模仿OOP继承。

class Parent extends Component {
  state = {happy: false}

  changeState(happy) {
    this.setState({happy})
  }

  render() {
    return (
      <Child parent={this} >
    )
  }
}

class Child extends Component {
   //...
   this.props.parent.changeState(true)
}

$ 0.02 乔恩

答案 6 :(得分:0)

我创建了一个组织所有“绑定”的方法。

class MyClass {
  constructor() {

    this.bindMethods([
      'updateLocationFields',
      'render',
      'loadCities',
    ]);
  }

  bindMethods(methods) {
    methods.forEach((item) => {
      this[item] = this[item].bind(this);
    });
  }

  ...
}

答案 7 :(得分:0)

我使用辅助函数 doBinding(this) ,我在每个构造函数中都调用了该函数。在此示例中,它绑定_handleChange1()_handleChange2()

class NameForm extends React.Component {
    constructor(props) {
        super(props);
        doBinding(this);
        this.state = {value1: "", value2: ""};
    }
    _handleChange1(event) {
        this.setState({value1: event.target.value});
    }
    _handleChange2(event) {
        this.setState({value2: event.target.value});
    }
    render() {
       ...
    }
}

即使您不使用Babel,该方法仍然有效。

我的处理程序方法都以_开头(约定表明它们是私有的)。因此doBinding()寻找_。如果不使用此约定,则可以删除if (key.startsWith("_"))

function doBinding(obj) {
    const proto = Object.getPrototypeOf(obj);
    for (const key of Object.getOwnPropertyNames(proto)) {
        if (key.startsWith("_")) {
            obj[key] = obj[key].bind(obj);
        }
    }
}

答案 8 :(得分:0)

如何使用通用函数来完成这样的绑定工作:

// common function:
function bind(self,methods){
     for(var key in methods){
       self[key] = methods[key].bind(self);
     }
}

// your class:
class MyClass {
     constructor() {
          bind(this,{
              someHandler1(event){ 
                //... 
              },
              someHandler2(event){ 
                //...
              }
          })
     }
}