【问题标题】:Are Lambda in JSX Attributes an anti-pattern?JSX 属性中的 Lambda 是反模式吗?
【发布时间】:2017-10-13 14:33:01
【问题描述】:

我今天开始使用新的 linter (tslint-react),它给了我以下警告:

“由于其渲染性能影响,在 JSX 属性中禁止使用 Lambda”

我知道这会导致每次渲染都会创建一个新函数。并且它可能会触发不必要的重新渲染,因为子组件会认为它的道具已更改。

但我的问题是,如何将参数传递给循环内的事件处理程序:

customers.map( c => <Btn onClick={ () => this.deleteCust(c.id) } /> );

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    绝对不是反模式。

    Lambda(箭头函数)对渲染性能没有影响。

    唯一有影响的是shouldComponentUpdate的实现。这个函数默认返回true,意味着组件总是被渲染。这意味着即使道具没有改变,组件仍然会再次渲染。这是默认行为。

    如果不实现shouldComponentUpdate,将箭头函数更改为绑定方法不会提高性能。

    确实,不使用箭头函数可以简化shouldComponentUpdate 的实现,它们不应该与PureComponent 一起使用,但它们不是反模式。它们可以简化许多模式,例如向函数添加附加参数时(例如,您在示例中所做的事情)。

    另请注意,React 有无状态组件,它们甚至无法实现 shouldComponentUpdate,因此它们总是被渲染。

    在真正发现性能问题之前,不要考虑性能影响。

    【讨论】:

    • 在调用渲染和实际的 DOM 突变之间存在差异。因为如果 render 的结果和之前一样,react 协调不会触及 DOM。但是当使用箭头函数时,渲染的结果会不一样,所以会发生 DOM 突变。
    • @Koen。我认为onClick 处理程序在更新 DOM 时不算数。 React 没有将函数直接绑定到 DOM,因为它有一个抽象层(带有合成事件)。因此改变函数应该不会对 DOM 产生影响,因为 DOM 不会改变。
    • 啊,有道理。
    【解决方案2】:

    我不确定为什么/不允许它们,但无论如何; Javascript 允许我们像这样在代码块中声明函数

    function mapCustomersToButton(c) {
      function handleBtnClick() {
        this.deleteCust(c.id);
      }
    
      return <Btn onClick={handleBtnClick} />
    }
    
    return customers.map(mapCustomersToButton);
    

    handleBtnClick 函数围绕从mapCustomersToButton 函数传递给它的c 对象创建一个闭包;保留引用。

    这等价于:

    return customers.map(c => <Btn onClick={() => this.deleteCust(c.id)} />);
    

    【讨论】:

      【解决方案3】:

      据我所知,即使您不使用React.PureComponentuseMemo,它也会对性能产生影响。当您在组件的 prop(JSX 属性)中定义匿名箭头函数(Lambda)时,每次渲染都会创建相同的函数,因此 JS 引擎无法重用它。 这种重新设计会降低性能,因为 JavaScript 的引擎垃圾收集器必须收集那些不必要的功能。

      还有其他几种方法表现相同。看看下面的例子:

      #1 Lamba approach
      customers.map( c => <Btn onClick={ () => this.deleteCust(c.id) } /> );
      
      #2 bind apprach
      customers.map( c => <Btn onClick={ this.deleteCust.bind(this, c.id) } /> );
      
      #3 call approach
      customers.map( c => <Btn onClick={ this.deleteCust.call(this, c.id) } /> );
      
      #4 apply approach
      customers.map( c => <Btn onClick={ this.deleteCust.apply(this, [c.id]) } /> );
      

      我会说推荐的方法是在被映射的组件内创建一个单独的函数。让我们修改一下您的示例:

      const Btn = ({ clicked, customer }) => {
        const buttonClickHandler = () => {
          clicked(customer.id)
        }
        return <button onClick={buttonClickHandler}>Click me!</button> 
      }
      
      const App = () => {
        return (
          <App>
            { customers.map(c => <Btn customer={c} clicked={deleteCust} />) }
          </App>
        )
      }
      

      所以现在,因为我们在常量中使用函数表达式而不是匿名函数(不能重用),所以 React 不会在每个新组件重新渲染时重新创建新函数,垃圾收集器可以休息!

      【讨论】:

        猜你喜欢
        • 2020-05-09
        • 2018-11-22
        • 1970-01-01
        • 1970-01-01
        • 2012-06-22
        • 2018-03-27
        • 2011-08-04
        • 2010-11-04
        相关资源
        最近更新 更多