Jak decydujesz, jak wybierasz pomiędzy tymi trzema w zależności od celu / rozmiaru / rekwizytów / zachowania naszych komponentów?
Rozszerzanie z React.PureComponent
lub React.Component
z niestandardową shouldComponentUpdate
metodą ma wpływ na wydajność. Korzystanie z bezpaństwowych komponentów funkcjonalnych jest wyborem „architektonicznym” i nie przynosi żadnych korzyści wydajności od razu po wyjęciu z pudełka.
W przypadku prostych, tylko prezentacyjnych komponentów, które muszą być łatwo ponownie użyte, preferuj bezstanowe komponenty funkcjonalne. W ten sposób masz pewność, że są one oddzielone od rzeczywistej logiki aplikacji, że są niezwykle łatwe do przetestowania i że nie mają nieoczekiwanych efektów ubocznych. Wyjątkiem jest sytuacja, gdy z jakiegoś powodu jest ich dużo lub jeśli naprawdę trzeba zoptymalizować ich metodę renderowania (ponieważ nie można zdefiniować shouldComponentUpdate
dla funkcjonalnego komponentu bezstanowego).
Rozszerz, PureComponent
jeśli wiesz, że twój wynik zależy od prostych rekwizytów / stanu („prosty” oznacza brak zagnieżdżonych struktur danych, ponieważ PureComponent wykonuje płytkie porównanie) ORAZ potrzebujesz / możesz uzyskać pewne ulepszenia wydajności.
Rozszerz Component
i zaimplementuj własne, shouldComponentUpdate
jeśli potrzebujesz wzrostu wydajności, wykonując niestandardową logikę porównania pomiędzy następnymi / bieżącymi rekwizytami i stanem. Na przykład możesz szybko przeprowadzić głębokie porównanie za pomocą lodash # isEqual:
class MyComponent extends Component {
shouldComponentUpdate (nextProps, nextState) {
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}
}
Wdrażanie własnych shouldComponentUpdate
lub rozszerzanie z nich PureComponent
to również optymalizacje i jak zwykle powinieneś zacząć je analizować tylko w przypadku problemów z wydajnością ( unikaj przedwczesnych optymalizacji ). Zasadniczo zawsze staram się przeprowadzać te optymalizacje, gdy aplikacja jest w stanie roboczym, a większość funkcji jest już zaimplementowana. O wiele łatwiej jest skupić się na problemach z wydajnością, gdy faktycznie przeszkadzają.
Więcej szczegółów
Funkcjonalne komponenty bezstanowe:
Są one definiowane za pomocą funkcji. Ponieważ nie ma stanu wewnętrznego dla komponentu bezstanowego, wynik (co jest renderowane) zależy tylko od rekwizytów podanych jako dane wejściowe dla tej funkcji.
Plusy:
Najprostszy możliwy sposób zdefiniowania komponentu w React. Jeśli nie musisz zarządzać żadnym stanem, po co zawracać sobie głowę klasami i dziedziczeniem? Jedną z głównych różnic między funkcją a klasą jest to, że dzięki funkcji masz pewność, że wynik zależy tylko od danych wejściowych (a nie od historii poprzednich wykonań).
Najlepiej w swojej aplikacji powinieneś dążyć do posiadania jak największej liczby elementów bezstanowych, ponieważ zwykle oznacza to, że przeniosłeś logikę poza warstwę widoku i przeniosłeś ją do czegoś takiego jak redux, co oznacza, że możesz przetestować swoją prawdziwą logikę bez konieczności renderowania czegokolwiek (znacznie łatwiejsze do przetestowania, bardziej wielokrotnego użytku itp.).
Cons:
Brak metod cyklu życia. Nie możesz zdefiniować componentDidMount
innych znajomych. Zwykle robisz to w komponencie nadrzędnym znajdującym się wyżej w hierarchii, abyś mógł zamienić wszystkie dzieci w bezpaństwowe.
Nie ma możliwości ręcznego kontrolowania, kiedy ponowne renderowanie jest potrzebne, ponieważ nie można go zdefiniować shouldComponentUpdate
. Ponowne renderowanie odbywa się za każdym razem, gdy komponent otrzymuje nowe rekwizyty (brak możliwości płytkiego porównania itp.). W przyszłości React może automatycznie optymalizować bezstanowe komponenty, na razie jest kilka bibliotek, z których można korzystać. Ponieważ komponenty bezstanowe są tylko funkcjami, jest to w zasadzie klasyczny problem „zapamiętywania funkcji”.
Referencje nie są obsługiwane: https://github.com/facebook/react/issues/4936
Składnik rozszerzający klasę PureComponent VS Zwykły składnik rozszerzający klasę Component:
React miał PureRenderMixin
kiedyś możliwość dołączenia do klasy zdefiniowanej za pomocą React.createClass
składni. Mixin po prostu zdefiniuje shouldComponentUpdate
wykonanie płytkiego porównania między następnymi rekwizytami i następnym stanem, aby sprawdzić, czy coś się tam zmieniło. Jeśli nic się nie zmieni, nie ma potrzeby ponownego renderowania.
Jeśli chcesz użyć składni ES6, nie możesz używać miksów. Więc dla wygody React wprowadził PureComponent
klasę, z której można dziedziczyć zamiast korzystać Component
. PureComponent
po prostu implementuje shouldComponentUpdate
w taki sam sposób jak PureRendererMixin
. Jest to przede wszystkim wygoda, więc nie musisz sam go wdrażać, ponieważ płytkie porównanie obecnego / następnego stanu z rekwizytami jest prawdopodobnie najczęstszym scenariuszem, który może dać ci kilka szybkich zwycięstw w wydajności.
Przykład:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
Jak widać, wyniki zależą od props.imageUrl
i props.username
. Jeśli w komponencie nadrzędnym renderujesz <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
przy użyciu tych samych rekwizytów, React będzie wywoływał za render
każdym razem, nawet jeśli wynik byłby dokładnie taki sam. Pamiętaj jednak, że React implementuje różnicowanie domen, więc DOM nie byłby w rzeczywistości aktualizowany. Mimo to wykonanie różnicowania domen może być kosztowne, więc w tym scenariuszu byłoby to marnotrawstwem.
Jeśli zamiast tego UserAvatar
element się wysuwa PureComponent
, wykonuje się płytkie porównanie. A ponieważ rekwizyty i nextProps są takie same, render
nie będą w ogóle wywoływane.
Uwagi na temat definicji „czystej” w React:
Ogólnie rzecz biorąc, „czysta funkcja” jest funkcją, która zawsze ocenia ten sam wynik przy tych samych danych wejściowych. Wynik (dla React, to jest zwracane przez render
metodę) nie zależy od historii / stanu i nie ma żadnych skutków ubocznych (operacji, które zmieniają „świat” poza funkcją).
W React komponenty bezstanowe niekoniecznie są czystymi komponentami zgodnie z powyższą definicją, jeśli nazwiesz „bezstanowy” komponent, który nigdy nie wywołuje this.setState
i nie używa this.state
.
W rzeczywistości PureComponent
możesz nadal wywoływać działania niepożądane podczas metod cyklu życia. Na przykład możesz wysłać zapytanie ajax wewnątrz componentDidMount
lub wykonać obliczenia DOM, aby dynamicznie dopasować wysokość div wewnątrz render
.
Definicja „Głupich komponentów” ma bardziej „praktyczne” znaczenie (przynajmniej w moim rozumieniu): głupi komponent „dostaje polecenie” co robić przez komponent nadrzędny za pomocą rekwizytów i nie wie, jak to zrobić, ale używa rekwizytów zamiast tego wywołania zwrotne.
Przykład „inteligentnego” 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>
}
}
Przykład „głupiego” AvatarComponent
:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
Na koniec powiedziałbym, że „głupi”, „bezpaństwowy” i „czysty” to całkiem różne pojęcia, które czasami mogą się nakładać, ale niekoniecznie, w zależności od twojego przypadku użycia.