【问题标题】:'React - ES6 way' of binding click handlers with parameters'React - ES6方式'将点击处理程序与参数绑定
【发布时间】:2017-04-24 09:03:09
【问题描述】:

我已经阅读了很多关于() => {} 语法的使用、构造函数中的绑定、道具中的绑定等的文章。但据我了解,绑定this 在性能方面的成本很高,而且做与箭头函数的自动绑定代价高昂,因为它每次都会创建一个新的匿名函数。

那么处理这个问题的最高效的“反应方式”是什么?

构造函数中的绑定似乎对于不需要传递参数的函数效果很好,如下所示:

constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
}

但是我们如何处理传递参数的绑定函数,而不将其绑定在 prop 中,如下所示:

<li onClick={this.handleClick.bind(this, item.id)} />{item.name}</li>

在构造函数中绑定this,然后在prop 中绑定nullundefined 是否会导致绑定函数只绑定一次?

如有任何误解,请随时纠正我。似乎这个问题的解决方案应该更广为人知和普遍......那就是如果我不只是生活在岩石下!

编辑:

即使有抽象,点击处理程序不是与每个单独的项目渲染绑定吗?

here 文章中,他们给出了这个例子来避免绑定点击处理程序,但是因为 React.createClass 会自动绑定方法,我看不出这实际上是如何绑定的每个项目都渲染?

var List = React.createClass({
  render() {
    let { handleClick } = this.props;
    // handleClick still expects an id, but we don't need to worry
    // about that here. Just pass the function itself and ListItem
    // will call it with the id.
    return (
      <ul>
        {this.props.items.map(item =>
          <ListItem key={item.id} item={item} onItemClick={handleClick} />
        )}
      </ul>
    );
  }
});

var ListItem = React.createClass({
  render() {
    // Don't need a bind here, since it's just calling
    // our own click handler
    return (
      <li onClick={this.handleClick}>
        {this.props.item.name}
      </li>
    );
  },

  handleClick() {
    // Our click handler knows the item's id, so it
    // can just pass it along.
    this.props.onItemClick(this.props.item.id);
  }
});

有人能解释一下吗? 这看起来不只是避免绑定每个 ListItem 渲染,但由于 React.createClass 中的自动绑定,它仍然可以吗?

我用class List extends Component 语法而不是createClass 尝试了这个示例,并且this.handleClick 未定义,因为handleClick 方法未绑定到类。

归根结底,这似乎只是消除了冗长,并没有通过减少方法绑定来真正提高性能...

【问题讨论】:

  • 性能在这里绝对不是问题。在看到任何性能问题之前,您必须以 60fps 的速度更新数千个元素,而 React 本身将成为您的瓶颈,而不是垃圾收集,所以没关系。
  • 我没有意识到这一点。感谢您的评论!

标签: javascript reactjs ecmascript-6


【解决方案1】:

为了你的&lt;li onClick={this.handleClick.bind(this, item.id)} /&gt;{item.name}&lt;/li&gt;

这通常意味着您需要另一个抽象层,即一个新的反应组件,它返回相同的元素,但您可以将 onClick 作为道具以及项目 ID 作为道具传递。然后在那个组件中你会调用this.props.onClick(this.props.id) 或者你格式化数据。

本文列出了每种绑定实例方法的方式之间的所有差异,以及每种方式对性能的影响https://medium.com/@housecor/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56#

【讨论】:

  • 嘿,谢谢你的回答!我阅读了那篇文章,并编辑了我的问题以解决问题!请看一下!感谢您的帮助
  • 如果您使用的是 ES6,我建议您使用正确的 babel 配置,以便能够使用您发布的文章中的 #4 或 #5 方式绑定您的实例方法。当您使用这些方式绑定时,绑定仅在组件被实例化时完成一次。就 React.createClass 而言,它是类似的方式,当组件被实例化时,实例方法会自动绑定到该实例。它不必为每个 ListItem 创建一个新的绑定,因为如果有意义的话,函数本身已经绑定到实例
【解决方案2】:

如果传递参数,则必须在render 函数中绑定,而不是在构造函数中。这可以使用绑定或箭头函数来完成。

<li onClick={this.handleClick.bind(this, item.id)} />{item.name}</li>

<li onClick={() => this.handleClick(item.id)} />{item.name}</li>

【讨论】:

  • 我明白这一点,正如我在问题中所展示的那样,但我想知道是否有办法不必必须为渲染
  • @jakeaaron 不,没有。我建议使用箭头函数
【解决方案3】:

这行得通吗? (使用 TypeScript)

<Input type="password" onChange={this.onPasswordInputChange('primary')} />;
<Input type="password" onChange={this.onPasswordInputChange('secondary')} />;


interface IOnPasswordInputChange {
  (value: string): void;
}

private onPasswordInputChange = (type: string): IOnPasswordInputChange => {
  return (value: string) => {
    this.setState({ [type]: value });
  };
}

【讨论】:

    【解决方案4】:
    onDelete(e) {
        const id = e.target.dataset.id;
    }
    
    <DropdownItem data-id={item.id} onClick={this.onDelete}><i className="fa fa-trash" /> Remove</DropdownItem>
    

    【讨论】:

      【解决方案5】:

      在语法上,我更喜欢箭头函数而不是绑定(尤其是因为只有箭头函数有助于将额外的参数传递给事件处理程序)。了解与箭头函数相关的性能损失后,我创建了一个实用函数,它接受箭头函数定义,这些定义将在组件的生命周期内缓存:

      utils.js

      export function createLambdaCache() {
        const result = {};
        return (key, lambda) => {
          if (!(key in result)) {
            result[key] = lambda;
          }
          return result[key];
        };
      }
      

      component.js

      class MyComponent extends React.Component {
        render() {
          return (
            ...  
            // below 'btnEvent' string is used as a key to cache the event reference for subsequent renders
            <button onClick={this.bindWithArgs('btnEvent', evt => this.clickHander(id, evt))} />
            ...
          );
        }
        clickHandler(id, evt) {
          console.log(id, evt);
        }
        bindWithArgs = createLambdaCache();
      }
      

      typescript 以上createLambdaCache的版本:

      type evtHandler<E> = (evt: E) => void;
      export function createLambdaCache<T = any>() {
        const result: {[key in keyof T]: evtHandler<any>} = {} as any;
        return <E>(key: keyof T, lambda: evtHandler<E>) => {
          if (!(key in result)) {
            result[key] = lambda;
          }
          return <evtHandler<E>>result[key];
        };
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多