Як ви вирішили, як ви обираєте між цими трьома на основі мети / розміру / реквізиту / поведінки наших компонентів?
Розширення з 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>
}
}
Зрештою, я б сказав, що "німий", "без громадянства" та "чистий" - це зовсім різні поняття, які іноді можуть перетинатися, але не обов'язково, залежно від випадків використання.