javascript – 在React组件中绑定方法的首选方法

截至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.绑定渲染()

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装饰

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

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

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

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

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

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

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

解决方法

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

你可能没有意识到的一种方法是我提出的一个方法(没有看到其他任何类似的东西)专门用于避免方法2和3在对某些数据数组执行.map时渲染组件列表需要在props上传递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中的props上传函数文字,这不仅因为您需要绑定它,而且还需要将当前元素传递给实例方法.在这种情况下,大多数人只是放弃了将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}
        />
      )
    })}
  }
}

WeakMaps是avoid a memory-leak这样的正确选择.并且在组件上存储新缓存而不是作为全局缓存(在其自己的模块中),如果/当您尝试缓存同样的方法时,可以避免遇到名称冲突的可能性跨多个组件的同一对象(此处为Cats)的名称.

在这里解释了这个问题的唯一解决办法:https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36.

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

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...