绑定React组件中方法的首选方法

Hof*_*ann 10 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} />
  }
}
Run Code Online (Sandbox Code Playgroud)

2. render()中的箭头函数

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

  render() {
    return <div onClick={()=>{this._eventHandler()}} />
  }
}
Run Code Online (Sandbox Code Playgroud)

3.绑定渲染()

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

  render() {
    return <div onClick={this._eventHandler.bind(this)} />
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  render() {
    return <div onClick={this._eventHandler} />
  }
}
Run Code Online (Sandbox Code Playgroud)

5. @autobind装饰

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

  render() {
    return <div onClick={this._eventHandler} />
  }
}
Run Code Online (Sandbox Code Playgroud)

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

由于在每个渲染和React diff算法上发生绑定,因此23具有性能影响

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

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

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

Der*_*ing 3

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

您可能不知道的一种方法是我提出的(没有见过其他人有类似的方法),专门用于在处理.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)}
      />
    ))}
  }
}
Run Code Online (Sandbox Code Playgroud)

目前还不清楚如何避免在.map这样的 props 上传递函数文字,不仅因为您需要 bind this,而且还需要将当前元素传递到实例方法中。大多数人遇到这种情况就会放弃PureKittyCat制作React.PureComponent.

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

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}
        />
      )
    })}
  }
}
Run Code Online (Sandbox Code Playgroud)

WeakMap 是避免内存泄漏的正确选择。并且在组件上存储新的缓存而不是作为全局缓存(在其自己的模块中)可以避免遇到名称冲突的可能性,如果/当您尝试跨多个组件为同一对象(此处为 s)缓存相同名称的方法Cat时。

我见过的这个问题的唯一其他解决方案在这里解释:https ://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36 。

两者都涉及一些烦人的设置,但这实际上是实现该场景渲染优化的唯一方法。