Are Lambda in JSX Attributes an anti-pattern?

前端 未结 3 535
刺人心
刺人心 2021-01-31 15:31

I started using a new linter today (tslint-react) and it is giving me the following warning:

\"Lambdas are forbidden in JSX attributes due to their rendering performance

相关标签:
3条回答
  • 2021-01-31 16:09

    Well as far as I know, it has an impact on performance even if you're not using React.PureComponent or useMemo. When you define anonymous arrow function (Lambda) in component's prop (JSX attribute), the same function is created on each render so that JS engine can't reuse it. That recreation slows don't performance because JavaScript's engine garbage collector has to collect those unneccessary functions.

    There are several other approaches which behaves the same. Take a look at example below:

    #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]) } /> );
    

    I would say the recommended approach is to create a separate function inside the component being mapped. Let's modify a bit your example:

    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>
      )
    }
    

    So now, since instead of anonymous function (which can't be reused) we're using function expression in a constant, React doesn't recreate new function every new component re render and the garbage collector can rest in piece!

    0 讨论(0)
  • 2021-01-31 16:20

    I am not sure on why they are / are not allowed, but regardless; Javascript allows us to declare functions within blocks of code like so

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

    The handleBtnClick function creates a closure around the c object being passed into it from the mapCustomersToButton function; preserving the reference.

    This is equivalent to the following:

    return customers.map(c => <Btn onClick={() => this.deleteCust(c.id)} />);
    
    0 讨论(0)
  • 2021-01-31 16:26

    Definitely not an antipattern.

    Lambdas (arrow functions) have no impact on rendering performance.

    The only thing that has an impact is the implementation of shouldComponentUpdate. This function returns true by default, meaning that the component is always rendered. That means that even if the props don't change, the component is still rendered again. And that's the default behavior.

    Changing arrow function to a bound method won't improve the performance if you don't implement shouldComponentUpdate.

    It's true that not using arrow functions can simplify the implementation of shouldComponentUpdate and they should not be used with PureComponent but they are not an antipattern. They can simplify many patterns, e.g. when adding an additional parameter to function (e.g. what you are doing in your example).

    Also note that React has stateless components which cannot even implement shouldComponentUpdate and therefore they are always rendered.

    Don't think about performance impact until you actually find a performance problem.

    0 讨论(0)
提交回复
热议问题