why shouldn't jsx props use arrow functions or bind?

  • Last Update :
  • Techknowledgy :

Using arrow functions or binding in JSX is a bad practice that hurts performance, because the function is recreated on each render. Whenever a function is created, the previous function is garbage collected. Rerendering many elements might create jank in animations. Arrow functions introduce concise body syntax, or implicit return. This allows the omission of the curly brackets and the return keyword. Implicit return is useful for creating succinct one-line operations in map , filter , and other common array methods.31-Jul-2020 Arrow functions are best for callbacks or methods like map, reduce, or forEach. You can read more about scopes on MDN. On a fundamental level, arrow functions are simply incapable of binding a value of this different from the value of this in their scope.28-Sept-2017

This is our Splunktool team suggestion ✌, we tried and its working fine
{
   "presets": ["module:metro-react-native-babel-preset"],
   "plugins": [
      ["@babel/plugin-proposal-decorators", {
         "decoratorsBeforeExport": false
      }],
      ["@babel/plugin-transform-arrow-functions", {
         "spec": true
      }]
   ]
}​

We’ll attempt to use programming in this lesson to solve the Which One Is Better Arrow Functions Or Using Bind In React? puzzle. This is demonstrated in the code below.

import React from 'react';
import { boundMethod as bind } from 'autobind-decorator';
class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  @bind
  clickHandler() {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}

The following piece of code provides a concise summary of the many methods that can be used to solve the Which One Is Better Arrow Functions Or Using Bind In React? problem.

{
   "presets": ["module:metro-react-native-babel-preset"],
   "plugins": [
      ["@babel/plugin-proposal-decorators", {
         "decoratorsBeforeExport": false
      }],
      ["@babel/plugin-transform-arrow-functions", {
         "spec": true
      }]
   ]
}

Suggestion : 2

A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it may cause unnecessary re-renders if a brand new function is passed as a prop to a component that uses reference equality check on the prop to determine if it should update. This will speed up rendering, as it avoids the need to create new functions (through bind calls) on every render. Note that this behavior is different for ref props, which is a special case in React that does not cause re-renders when a brand new function is passed. See ignore-refs below for more information.

1._
<Foo onClick={this._handleClick.bind(this)}></Foo>
2._
<Foo onClick={() => console.log('Hello!')}></Foo>
3._
function onClick() { console.log('Hello!'); }
<Foo onClick={onClick} />
5._
"react/jsx-no-bind": [<enabled>, {
  "ignoreDOMComponents": <boolean> || false,
  "ignoreRefs": <boolean> || false,
  "allowArrowFunctions": <boolean> || false,
  "allowFunctions": <boolean> || false,
  "allowBind": <boolean> || false
}]
6._
<div onClick={this._handleClick.bind(this) />
<span onClick={() => console.log("Hello!")} />
<button type="button" onClick={function() { alert("1337") }} />

Suggestion : 3

The fix is pretty easy: instead of defining an arrow function or invoking bind(), we need to use the same function every time again. We do that by referencing the function instead of invoking it: onClick={ this.onClick }. Note that there are no parentheses or brackets after the name of the function. If rendering a component defines a new function, this means that given the same set of props, defining a new function inside the rendering yields a different result every time you invoke that rendering and thus violates the aforementioned rule. Back to the arrow functions: every time the render() function of Parent is invoked, the <Button> component will receive a different function for its onClick prop. And that means it will be re-rendered every time again. This also happens when the onClick prop would have been this.onClick.bind(this), since that also creates a new function.

1._
class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    console.log('render button');
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }

  onClick() {
    this.setState((prevState) => ({
      counter: prevState.counter + 1
    }));
  }

  render() {
    const { counter } = this.state;

    return (
      <div>
        <Button onClick={ () => this.onClick() } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
2._
class List extends React.Component {
  render() {
    const { onItemClick } = this.props;
    return (
      <ul>
        { this.props.items.map(item =>
          <li key={ item.id } onClick={ item => onItemClick(item) }>
            { item.name }
          </li>
        )}
      </ul>
    );
  }
}

const items = [
  { id: 1, name: 'One' },
  { id: 2, name: 'Two' },
]
ReactDOM.render(
  <List items={ items } />,
  document.getElementById('root')
);
3._
class Item extends React.Component {
  onClick() {
    this.props.onClick(this.props.item)
  }

  render() {
    return (
      <li onClick={ this.onClick }>{ item.name }</li>
    );
  }
);

class List extends React.Component {
  render() {
    const { onItemClick } = this.props;
    return (
      <ul>
        { this.props.items.map(item =>
          <Item key={ item.id } onClick={ this.onItemClick } item={ item } />
        )}
      </ul>
    );
  }
}
5._
class Demo {
   private myName = 'Example';

   public example1() {
      console.log(`Example 1: ${this.myName}`);
   }

   public example2 = () => {
      console.log(`Example 2: ${this.myName}`);
   }
}
6._
var Demo = (function() {
   function Demo() {
      var _this = this;
      this.myName = 'Example';
      this.example2 = function() {
         console.log("Example 2: " + _this.myName);
      };
   }
   Demo.prototype.example1 = function() {
      console.log("Example 1: " + this.myName);
   };
   return Demo;
}());

Suggestion : 4

Whenever a function is created, the previous function is garbage collected. Rerendering many elements might create jank in animations. Note: the useState generated function accepts an updater function, that provides the current state. In this way, we don't need to set the current state a dependency of useCallback. Binding a method using the proposal-class-fields with an arrow function. As this is a stage 3 proposal, you'll need to add the Stage 3 preset or the Class properties transform to your babel configuration.

error JSX props should not use arrow functions react / jsx - no - bind

Suggestion : 5

Whenever a function is created, the previous function is garbage collected. Rerendering many elements might create jank in animations. Traditionally, performance concerns around inline functions in React have been related to how passing new callbacks on each render breaks shouldComponentUpdate optimizations in child components. (docs) Performance issues with Function.prototype.bind got fixed here and arrow functions are either a native thing or are transpiled by babel to plain functions; in both cases we can assume it’s not slow. (React Training)

1._
class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  onClick = () => this.setState((prevState) => ({
    counter: prevState.counter + 1
  }));
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ this.onClick } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
2._
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
3._
class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ () => this.setState((prevState) => ({
          counter: prevState.counter + 1
        })) } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);

Binding a method using the proposal-class-fields with an arrow function. As this is a stage 3 proposal, you'll need to add the Stage 3 preset or the Class properties transform to your babel configuration.

class Button extends React.Component {
  cb = () => { // the class property is initialized with an arrow function that binds this to the class

  }

  render() {
    return (
      <button onClick={ this.cb }>Click</button>
    );
  }
}
6._
const { memo, useState } = React;

const Button = memo(({ onClick }) => console.log('render button') || (
  <button onClick={onClick}>Click</button>
));

const Parent = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
  
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);