Являются ли 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 не воссоздает новую функцию при каждом рендеринге нового компонента, и сборщик мусора может собираться по частям!