Почему JSX props не должен использовать функции стрелки или привязки?

я запускаю Линт с моим приложением React, и я получаю эту ошибку:

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

и здесь я запускаю функцию стрелки (внутри onClick):

{this.state.photos.map(tile => (
  <span key={tile.img}>
    <Checkbox
      defaultChecked={tile.checked}
      onCheck={() => this.selectPicture(tile)}
      style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
    />
    <GridTile
      title={tile.title}
      subtitle={<span>by <b>{tile.author}</b></span>}
      actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
    >
      <img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
    </GridTile>
  </span>
))}

это плохая практика, которую следует избегать? И как это лучше всего сделать?

3 ответов


почему вы не должны использовать встроенные функции стрелки в JSX props

использование функций стрелки или привязки в JSX-плохая практика, которая вредит производительности, потому что функция воссоздается на каждом рендеринге.

  1. всякий раз, когда создается функция, предыдущая функция-это собранный мусор. Перезапуск многих элементов может создать jank в анимации.

  2. использование встроенной функции стрелки вызовет PureComponentS, и компоненты, которые используют shallowCompare на shouldComponentUpdate способ все равно повторно. Поскольку функция стрелки prop воссоздается каждый раз, неглубокое сравнение идентифицирует его как изменение в prop, а компонент будет повторно преобразован.

как вы можете видеть в следующих 2 примерах-когда мы используем встроенную функцию стрелки,<Button> компонент перенаправляется каждый раз (на консоли отображается текст "кнопка рендеринга").

Пример 1-PureComponent без встроенный обработчик

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')
);
<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>

Пример 2-PureComponent С встроенный обработчик

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')
);
<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>

методы привязки к this без встроенных функций стрелки

  1. привязка метода вручную в конструкторе:

    class Button extends React.Component {
      constructor(props, context) {
        super(props, context);
    
        this.cb = this.cb.bind(this);
      }
    
      cb() {
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
    
  2. привязка a метод с использованием


Это потому, что функция стрелки, по-видимому, создаст новый экземпляр функции для каждого рендеринга, если он используется в свойстве JSX. Это может создать огромную нагрузку на сборщик мусора, а также помешать браузеру оптимизировать любые "горячие пути", поскольку функции будут выброшены вместо повторного использования.

вы можете увидеть все объяснение и некоторую дополнительную информацию на https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md


чтобы избежать создания новых функций с теми же аргументами, вы можете запомнить результат привязки функции, вот простая утилита с именем memobind для этого: https://github.com/supnate/memobind