【问题标题】:Preferred way to bind methods in React Components在 React 组件中绑定方法的首选方式
【发布时间】:2017-11-30 09:02:47
【问题描述】:

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

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

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

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

1 是最安全的方式,因为它不需要 babel 的构建时转换,但打字很烦人。

23 由于每次渲染和 React diff 算法的绑定都会对性能产生影响

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

在最新的 Babel 版本中,45 是推荐和最安全(关于未来兼容性)的绑定函数方式吗?还有其他我不知道的方法吗?我应该继续使用 1 吗?此外,如果这些被认为可以安全使用,是否有任何代码模块可以更改我的代码库以使用它们?

编辑:@LucaFabbri 指向reflective bind babel 变换。它看起来很酷,但它需要一个我不喜欢的非标准 babel-plugin,因为它对未来不是很安全。我尽量避免构建时魔法,如果您长时间只在一个代码库上工作,则可以使用它们,但如果您维护多个代码库,则每次都需要处理构建时魔法(加上不支持 create-react-app 而不弹出)。

【问题讨论】:

  • 我认为您已经强调了每种方法的所有优点和缺点。看过很多代码库后,没有一种方法可以解决这个问题,所以我预计至少 1、2 和 3 会持续很长时间。在我看来,如果您关心的是面向未来,请坚持使用 1 或 3。
  • 我尝试了各种方法。对我来说,不费吹灰之力的选择是 #4,因为我更喜欢干净的代码。
  • 同意安杰洛斯。此外,我认为没有任何理由强调方法,因为没有真正的公共/私人差异
  • 我认为#4 最终会被优化并变得更加流行。虽然没有确凿的数据,只是观察空间的轶事证据
  • 阅读flexport.engineering/… 以使用新方法扩展您的分析

标签: javascript reactjs


【解决方案1】:

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

您可能不知道的一种方法是我提出的一种方法(没有见过其他人有类似的方法),专门用于在对一些数据数组执行.map 时避免方法 2 和 3 以呈现出来需要在 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 上传递函数文字,不仅因为您需要绑定 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}
        />
      )
    })}
  }
}

WeakMaps 是 avoid a memory-leak 这样的事情的正确选择。如果/当您尝试为同一对象缓存同名方法时(此处为Cats)跨多个组件。

我见过的解决此问题的唯一其他解决方案如下:https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36

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

【讨论】:

  • 哇,这太聪明了,我以前从来没有任何理由使用 Wea​​kMap。但是你不应该在 componentWillReceiveProps 或 componentWillUpdate 中调用 this.methodCache.set() 吗?在渲染中这样做感觉不对。只是想补充一点,渲染中的箭头函数还不错(性能影响可以忽略不计,我记得读过 Dan Abramov 的推文,在那里测试过),不好的是箭头函数作为道具。所以Array.map(() =&gt; {}) 可以,但&lt;Component onClick={()=&gt;{...} /&gt; 不行
  • @Hoffmann,感谢您的回复!在.render 中执行此操作确实是调用this.methodCache.set(obj, {}) 的正确位置,因为关键部分是您传入的obj 以唯一地与某些方法相关联,而那些objs 在componentWillReceiveProps 中不可用或componentWillUpdate。而且我认为你已经混淆了.render 中的箭头函数与props 中的箭头函数的一些事实。我会指出你 herehere 以获得一些澄清。
猜你喜欢
  • 2016-09-14
  • 1970-01-01
  • 2021-10-16
  • 1970-01-01
  • 2019-02-10
  • 2010-11-09
  • 2018-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多