Являются ли Lambda в атрибутах JSX анти-шаблоном?

Я начал использовать новый linter сегодня (tslint-react), и он дает мне следующее предупреждение:

"Lambdas запрещены в атрибутах JSX из-за воздействия производительности рендеринга"

Я понимаю, что это приводит к созданию новой функции с каждым рендерингом. И это может вызвать ненужные повторные рендеринги, потому что дочерний компонент будет думать, что реквизиты изменились.

Но мой вопрос в том, как еще можно передать параметры обработчику событий внутри цикла:

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

Ответ 1

Определенно не антипаттерн.

Lambdas (функции стрелок) не влияют на производительность рендеринга.

Единственное, что имеет влияние, это реализация shouldComponentUpdate. Эта функция возвращает true по умолчанию, что означает, что компонент всегда отображается. Это означает, что даже если реквизит не изменится, компонент все еще отображается снова. И это поведение по умолчанию.

Изменение функции стрелки на связанный метод не улучшит производительность, если вы не реализуете shouldComponentUpdate.

Верно, что не использование функций стрелок упрощает реализацию shouldComponentUpdate, и они не должны использоваться с PureComponent, но они не являются антипаттернами. Они могут упростить многие шаблоны, например. при добавлении дополнительного параметра в функцию (например, что вы делаете в своем примере).

Также обратите внимание, что React имеет безстоящие компоненты, которые даже не могут реализовать shouldComponentUpdate, и поэтому они всегда отображаются.

Не думайте о влиянии производительности, пока не найдете проблемы с производительностью.

Ответ 2

Я не уверен, почему они/не разрешены, но независимо; Javascript позволяет нам объявлять функции внутри блоков кода, как это

function mapCustomersToButton(c) {
  function handleBtnClick() {
    this.deleteCust(c.id);
  }

  return <Btn onClick={handleBtnClick} />
}

return customers.map(mapCustomersToButton);

Функция handleBtnClick создает замыкание вокруг объекта c, передаваемого в него из функции mapCustomersToButton; сохранение ссылки.

Это эквивалентно следующему:

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

Ответ 3

Ну, насколько я знаю, это влияет на производительность, даже если вы не используете React.PureComponent или useMemo. Когда вы определяете функцию анонимной стрелки (Lambda) в компоненте prop (атрибут JSX), одна и та же функция создается при каждом рендеринге, поэтому движок 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 не воссоздает новую функцию при каждом рендеринге нового компонента, и сборщик мусора может собираться по частям!