Як ви вирішили, як ви обираєте між цими трьома на основі мети / розміру / реквізиту / поведінки наших компонентів?
Розширення з React.PureComponent
або від React.Component
користувальницького shouldComponentUpdate
методу має наслідки для продуктивності. Використання функціональних компонентів без стану - це "архітектурний" вибір і не має жодних переваг від продуктивності (поки що).
Для простих, лише презентаційних компонентів, які потрібно легко використовувати повторно, віддайте перевагу функціональним компонентам без стану. Таким чином, ви впевнені, що вони відокремлені від фактичної логіки програми, що їх легко перевірити і що у них немає несподіваних побічних ефектів. Виняток - якщо з якихось причин їх багато, або якщо вам дійсно потрібно оптимізувати їх метод візуалізації (так як ви не можете визначити shouldComponentUpdate
функціональний компонент без стану).
Розширення, PureComponent
якщо ви знаєте, що ваш результат залежить від простого реквізиту / стану ("простий" означає відсутність вкладених структур даних, оскільки PureComponent виконує неглибоке порівняння) І вам потрібно / можна отримати деякі покращення продуктивності.
Розширюйте Component
та реалізовуйте власні, shouldComponentUpdate
якщо вам потрібні певні покращення продуктивності, виконуючи власну логіку порівняння між наступними / поточними реквізитами та станом. Наприклад, ви можете швидко виконати глибоке порівняння, використовуючи lodash # isEqual:
class MyComponent extends Component {
shouldComponentUpdate (nextProps, nextState) {
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}
}
Крім того, оптимізація - це власна реалізація shouldComponentUpdate
чи продовження PureComponent
, і, як завжди, ви повинні почати розглядати це лише у випадку, якщо у вас є проблеми з продуктивністю ( уникайте передчасних оптимізацій ). Як правило, я завжди намагаюся зробити ці оптимізації після того, як додаток перебуває в робочому стані, при цьому більшість функцій вже реалізовані. Набагато простіше зосередитись на проблемах із продуктивністю, коли вони насправді заважають.
Детальніше
Функціональні компоненти без стану:
Вони визначаються лише за допомогою функції. Оскільки для компонента без стану немає внутрішнього стану, вихід (що надається) залежить тільки від реквізиту, заданого як вхід до цієї функції.
Плюси:
Найпростіший можливий спосіб визначення компонента в React. Якщо вам не потрібно керувати жодною державою, навіщо турбуватися з класами та спадщиною? Однією з головних відмінностей між функцією та класом є те, що з функцією ви впевнені, що результат залежить лише від введення даних (а не від історії попередніх виконань).
В ідеалі у вашому додатку ви повинні мати на меті мати якомога більше компонентів без стану, оскільки це означає, що ви перемістили свою логіку за межі шару перегляду та перемістили її на щось подібне до редуксу, а це означає, що ви можете перевірити свою справжню логіку, не вимагаючи нічого рендерувати (набагато простіше тестувати, більше використовувати багаторазово тощо).
Мінуси:
Жодних методів життєвого циклу. У вас немає способу визначити componentDidMount
та інших друзів. Зазвичай ви робите це в батьківському компоненті вище в ієрархії, щоб ви могли перетворити всіх дітей на дітей без громадянства.
Ні в якому разі не можна вручну керувати, коли повторне відображення не потрібно, оскільки ви не можете визначитись shouldComponentUpdate
. Повторне відображення відбувається щоразу, коли компонент отримує нові реквізити (ніякого способу дрібного порівняння тощо). Надалі React може автоматично оптимізувати компоненти без стану, оскільки зараз ви можете використовувати деякі бібліотеки. Оскільки компоненти без громадянства є лише функціями, в основному це класична проблема "функціонування запам'ятовування".
Посилання не підтримуються: https://github.com/facebook/react/isissue/4936
Компонент, що розширює клас PureComponent VS Нормальний компонент, який розширює клас компонентів:
React використовувався для того, щоб PureRenderMixin
ви могли приєднатись до класу, визначеного за допомогою React.createClass
синтаксису. Mixin просто визначить shouldComponentUpdate
виконання неглибокого порівняння між наступним реквізитом і наступним станом, щоб перевірити, чи щось там змінилося. Якщо нічого не змінюється, повторно відтворювати не потрібно.
Якщо ви хочете використовувати синтаксис ES6, ви не можете використовувати міксини. Тож для зручності React представив PureComponent
клас, який можна успадкувати, а не використовувати Component
. PureComponent
просто реалізує shouldComponentUpdate
таким же чином PureRendererMixin
. Це здебільшого зручність, тому вам не доведеться реалізовувати це самостійно, оскільки неглибоке порівняння між поточним / наступним станом та реквізитом - це, мабуть, найпоширеніший сценарій, який може принести вам швидкі виграші в продуктивності.
Приклад:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
Як ви бачите, результат залежить від props.imageUrl
і props.username
. Якщо в батьківському компоненті ви рендеруєте <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
з однаковими реквізитами, React дзвонить render
кожен раз, навіть якщо вихід буде абсолютно однаковим. Пам'ятайте, що реалізація реалізує dom-різницю, тому DOM фактично не оновлювався. І все-таки виконання дому різного може бути дорогим, тому в цьому сценарії було б марно.
Якщо UserAvatar
компонент поширюється PureComponent
натомість, виконується неглибоке порівняння. А оскільки реквізит і nextProps однакові, render
взагалі не будуть називатися.
Примітки щодо визначення поняття "чистий" в React:
Загалом "чиста функція" - це функція, яка завжди оцінює один і той же результат, даючи один і той же вхід. Вихід (для React, це те, що повертається render
методом) не залежить від історії / стану і не має побічних ефектів (операції, що змінюють "світ" поза функцією).
У React компоненти без стану не обов'язково є чистими компонентами згідно з вищезазначеним визначенням, якщо ви називаєте "без громадянства" компонент, який ніколи не викликає this.setState
і не використовує this.state
.
Насправді, у програмі PureComponent
ви все одно можете виконувати побічні ефекти під час методів життєвого циклу. Наприклад, ви можете надіслати запит ajax всередині componentDidMount
або ви можете виконати деякий обчислення DOM, щоб динамічно регулювати висоту діла всередині render
.
Визначення "тупих компонентів" має більш "практичне" значення (принаймні, наскільки я розумію): німий компонент "отримує відповідь", що робити з батьківським компонентом через реквізит, і не знає, як робити речі, але використовує реквізит замість цього.
Приклад "розумного" AvatarComponent
:
class AvatarComponent extends Component {
expandAvatar () {
this.setState({ loading: true });
sendAjaxRequest(...).then(() => {
this.setState({ loading: false });
});
}
render () {
<div onClick={this.expandAvatar}>
<img src={this.props.username} />
</div>
}
}
Приклад "німого" AvatarComponent
:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
Зрештою, я б сказав, що "німий", "без громадянства" та "чистий" - це зовсім різні поняття, які іноді можуть перетинатися, але не обов'язково, залежно від випадків використання.