在React Components

时间:2017-11-30 09:02:48

标签: javascript reactjs

截至2017年11月,我了解了几种将方法绑定到React组件的方法,以便this关键字指向拥有该方法的React Element(例如,在事件处理程序中必需)

1。在构造函数中绑定

class A extends React.Component {
  constructor(props) {
    super(props)
    this._eventHandler = this._eventHandler.bind(this)
  }

  _eventHandler() {
    // ...
  }

  render() {
    return <div onClick={this._eventHandler} />
  }
}

2。 render()中的箭头函数

class A extends React.Component {
  _eventHandler() {
    // ...
  }

  render() {
    return <div onClick={()=>{this._eventHandler()}} />
  }
}

第3。在render()中绑定

class A extends React.Component {
  _eventHandler() {
    // ...
  }

  render() {
    return <div onClick={this._eventHandler.bind(this)} />
  }
}

4。 ES2015类字段中的箭头功能

class A extends React.Component {
  _eventHandler = () => {
    // ...
  }

  render() {
    return <div onClick={this._eventHandler} />
  }
}

5。 @autobind decorator

class A extends React.Component {
  @autobind
  _eventHandler() {
    // ...
  }

  render() {
    return <div onClick={this._eventHandler} />
  }
}

1 是最安全的方式,因为它不需要babel进行构建时转换,但输入时非常烦人。

2 3 由于每个渲染和React差异算法上发生绑定而导致性能影响

4 5 需要的输入要少于 1 ,但它们需要babel支持,可能不是最终规格的一部分然而。除此之外,我非常反对注释的想法(来自Java后端背景我鄙视注释,因为它们经常被过度使用和过于神奇)

截至最新的Babel版本是 4 还是 5 建议的和最安全的(关于未来兼容性)绑定功能的方式?还有其他我不知道的方式吗?我应该继续使用 1 吗?另外,如果任何被认为可以安全使用的是有任何可以改变我的代码库以使用它们的代码模块吗?

编辑:@LucaFabbri指向reflective bind babel变换。它看起来很酷,但它需要一个非标准的babel插件,我不喜欢它,因为它不是非常安全的。我试图尽可能地避免构建时魔术,如果你在很长一段时间内只使用一个代码库就可以使用它们,但如果你维护几个代码库,你需要每次都处理构建时魔术(加上没有弹出的create-react-app支持。

1 个答案:

答案 0 :(得分:3)

如果构造函数(方法1)中的绑定对你来说太烦人了,我会说首选方法是类字段上的箭头函数(方法4),因为它是一个简单的babel变换,{{3} (基本上是面向未来的),并避免方法2和3的性能问题(如果您希望利用shouldComponentUpdatePureComponent

您可能不知道的一种方法是我提出的(没有看到其他任何类似的东西)专门用于在对某些数据数组执行.map时避免方法2和3的方法渲染出需要在道具上传递this.someInstanceMethod(withSomeArg)的组件列表。例如:

class CatList extends React.Component {
  static propTypes = {
    kitties: PropTypes.arrayOf(PropTypes.instanceOf(Cat)),
  }

  adoptKitty(cat) {
    this.setState({ loading: true })
    return api.adopt(cat)
      .then(res => this.setState({ loading: false })
      .catch(err => this.setState({ loading: false, err })
  }

  render() {
    // ... other stuff ...

    {this.props.kitties.map(kitty => (
      <PureKittyCat 
        key={kitty.id} 
        // ... the problem:
        onClick={() => this.adoptKitty(kitty)}
      />
    ))}
  }
}

目前还不清楚如何避免在.map这样的道具上传递函数文字,不仅因为你需要绑定this,而且你还需要将当前元素传递给实例方法。在这种情况下,大多数人都会放弃让PureKittyCat成为React.PureComponent

的想法

我对此问题的解决方案是在Component实例上存储WeakMap以创建本地缓存(父组件的本地缓存),该缓存将每个kitty对象与我想要传递给关联的{{}的任何方法相关联。 1}}组件。它看起来像这样:

PureKittyCat

WeakMaps是it's a stage 3 proposal之类的正确选择。并且在组件上而不是作为全局缓存(在其自己的模块中)存储新缓存,如果/当您尝试缓存同一对象的同名方法时,可以避免遇到名称冲突(此处class CatList extends React.Component { static propTypes = { kitties: PropTypes.arrayOf(PropTypes.instanceOf(Cat)), } this.methodCache = new WeakMap() adoptKitty(cat) { this.setState({ loading: true }) return api.adopt(cat) .then(res => this.setState({ loading: false }) .catch(err => this.setState({ loading: false, err }) } render() { // ... other stuff... {this.props.kitties.map(kitty => { // ... the good stuff: if ( !this.methodCache.has(kitty) ) { this.methodCache.set(kitty, { adopt: () => this.adoptKitty(kitty), // any other methods you might need }) } // as long is this is the same in-memory kitty, onClick will // receive the same in-memory function object every render return ( <PureKittyCat key={kitty.id} onClick={this.methodCache.get(kitty).adopt} /> ) })} } } s)跨多个组件。

我在这里解释了这个问题的唯一解决方案:avoid a memory-leak

两者都涉及一些讨厌的设置,但这确实是实现该场景的渲染优化的唯一方法。