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

Я запускаю lint с моим приложением 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>
))}

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

Ответы на вопрос(5)

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

Использовать встроенные функции, как это, прекрасно. Правило линтинга устарело.

Это правило относится ко времени, когда функции стрелок были не такими распространенными, и люди использовали .bind (это), который раньше был медленным. Проблема производительности была исправлена ​​в Chrome 49.

Обратите внимание, что встроенные функции не передаются в качестве подпорки дочернему компоненту.

Райан Флоренс, автор React Router, написал отличную статью об этом:

https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578

 sbaechler20 авг. 2019 г., 10:36
@krankuba Вопрос не в этом. Вы по-прежнему можете передавать анонимные функции, которые не определены как встроенные, но все еще не проверяемы.
 krankuba09 апр. 2019 г., 17:46
Не могли бы вы показать, как написать модульный тест для компонентов со встроенными функциями стрелки?

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

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

 apieceofbart04 июн. 2018 г., 13:30
«Создание новых экземпляров функции каждый раз означает, что состояние изменяется», что вы подразумеваете под этим? Там нет государства вообще в вопросе
 hippietrail23 сент. 2017 г., 04:07
Не только это. Создание новых экземпляров функции каждый раз означает, что состояние изменяется, а когда состояние компонента изменяется, оно будет перерисовано. Поскольку одной из основных причин использования React является визуализация только изменяемых элементов, используяbind или стрелка функции здесь стреляет себе в ногу. этоне хотя хорошо документировано, особенно в случае работы сmapping-массивы в списках и т. д.

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

Примечание: внутренне он кэширует ваши функции стрелок указанным ключом, не нужно беспокоиться о повторном рендеринге!

render() {

  return <div>
  {
        this.props.photos.map(photo=>
          <Photo key={photo.url}
            onClick={this.handler(photo.url, (url) => { 
                 console.log(url) })}
          />)
   }
 </div>

}

Другие преимущества:

Именованные обработчикиОбрабатывать события с помощью стрелокДоступ к ключу, пользовательским аргументам и исходному событиюКомпонент рендеринга производительностиПользовательский контекст для обработчиков
Решение Вопроса

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

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

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

Использование встроенной функции стрелки приведет кPureComponentи компоненты, которые используютshallowCompare вshouldComponentUpdate В любом случае, метод перерисовки. Поскольку функция стрелки опора воссоздается каждый раз, поверхностное сравнение идентифицирует ее как изменение реквизита, и компонент будет перерисовываться.

Как вы можете видеть в следующих 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/[email protected]/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/[email protected]/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/[email protected]/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<div id="root"></div>

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

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

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

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

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>
    );
  }
}
 Ori Drori17 апр. 2016 г., 17:12
Добро пожаловать. Используй реаги-редукс иmapDispatchToProps в смарт-компонент, созданныйconnect чтобы создать функции, которые вы передаете реквизиту.
 philk28 окт. 2016 г., 14:40
@OriDrori, но считается ли это плохим стилем или почему я никогда не вижу этого в уроках и примерах?
 lux17 апр. 2016 г., 17:04
Как вы достигаете этого на компонентах без состояния?
 Ori Drori28 окт. 2016 г., 15:04
Понятия не имею, но, возможно, свойства класса и привязка в конструкторе сохраняют формат класса, то есть методы в теле класса, в то время как представленный вами метод перемещает все в конструктор. Впрочем, я просто догадываюсь.
 Ori Drori13 февр. 2019 г., 14:32
Сборка мусора может повлиять на оба типа анимации, насколько я знаю.
 Ori Drori25 окт. 2016 г., 06:39
@philk - Действительно. Это эквивалентно объявлению полей экземпляра класса.
 philk24 окт. 2016 г., 23:31
было быconstructor(props) { this.onTodoClick = () => this.props.onTodoClick(this.props.id) } работать тоже?
 lux17 апр. 2016 г., 17:15
На самом деле, но если подумать, я просто не предоставляю обратный вызов вonClickЯ всегда привязываюсь к функции стрелки по привычке, думая, что в противном случае она будет вызвана немедленно. Спасибо за чаевые.
 Ori Drori09 сент. 2016 г., 15:14
На самом деле это не так. Вы должны связать их вручную, используя один из методов в ответе.
 Fabian Zeindl09 сент. 2016 г., 15:11
В моем тесте прямо сейчас функции стрелки экземпляра класса не привязаны кthis.
 Ori Drori13 мая 2016 г., 00:54
@ adam-beck - добавьте его в определение метода обратного вызова в классеcb() { onTodoClick(this.props.todo.id); }.
 Devashish13 февр. 2019 г., 13:39
«Повторное рендеринг многих элементов может создать эффект анимации». Это относится к анимации, выполненной только с использованием JS, или к CSS-анимации?
 Ori Drori17 апр. 2016 г., 17:06
Компоненты без состояния (функции) не имеютthis, так что нечего связывать. Обычно методы предоставляются интеллектуальным компонентом-оболочкой.
 Ori Drori14 мая 2016 г., 22:35
Используйте компонент класса и используйте один из его методов в качестве обратного вызова. CB затем сможет использовать одно из свойств класса в качестве идентификатора.
 lux17 апр. 2016 г., 17:10
Имеет смысл. У меня есть некоторые компоненты без сохранения состояния, которые отправляют действия для редукции, но через функции стрелок в компоненте, а не через пропущенные реквизиты. Похоже, я мог бы реорганизовать рефакторинг, чтобы вместо этого передать функцию и получить немного производительности. Благодарю.
 Shota Tamura24 авг. 2019 г., 06:13
@ Adam-Beck Я думаю, что это как использоватьuseCallback с динамическим значением.stackoverflow.com/questions/55006061/...
 adam-beck13 мая 2016 г., 01:35
Это самый низкий уровень, который у меня есть, и это просто компонент без состояния. Это то, что вы имели в виду?gist.github.com/adam-beck/686bd0e4f495abcbbdcd0fab41f68eb6
 adam-beck12 мая 2016 г., 22:14
@OriDrori: Как это работает, когда вам нужно передать данные в обратном вызове?onClick={() => { onTodoClick(todo.id) }
 Ori Drori24 июн. 2019 г., 19:23
@ adam-beck - я добавил решение с использованием хуков, которые обращаются к передаваемым данным в вопросе обратного вызова.

Ваш ответ на вопрос