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.
props
. przykładem .PureComponent
nie powinieneś implementowaćshouldComponentUpdate()
. Powinieneś zobaczyć ostrzeżenie, jeśli faktycznie to zrobisz.PureComponent
komponentów, które mają zagnieżdżone właściwości obiektu / tablicy. Oczywiście musisz zdawać sobie sprawę z tego, co się dzieje. Jeśli dobrze rozumiem, jeśli nie mutujesz rekwizytów / stanu bezpośrednio (co React próbuje uniemożliwić robieniu ostrzeżeń) lub za pośrednictwem zewnętrznej biblioteki, powinieneś dobrze używaćPureComponent
zamiastComponent
prawie wszędzie ... z wyjątkiem bardzo prostych komponentów, w których może faktycznie być szybszy NIE używać - patrz news.ycombinator.com/item?id=14418576nie jestem geniuszem zbyt reagującym, ale z mojego zrozumienia możemy korzystać z każdego elementu w następujących sytuacjach
Komponent bezstanowy - są to komponenty, które nie mają cyklu życia, dlatego te komponenty powinny być używane do renderowania powtarzającego się elementu komponentu nadrzędnego, takiego jak renderowanie listy tekstowej, która po prostu wyświetla informacje i nie ma żadnych działań do wykonania.
Czysty komponent - są to przedmioty, które mają cykl życia i zawsze zwracają ten sam wynik, gdy podany zostanie określony zestaw rekwizytów. Komponentów tych można używać podczas wyświetlania listy wyników lub konkretnych danych obiektowych, które nie mają złożonych elementów potomnych i używanych do wykonywania operacji, które wpływają tylko na siebie. taka wyświetlająca lista kart użytkownika lub lista kart produktów (podstawowe informacje o produkcie) i tylko działanie, które użytkownik może wykonać, to kliknąć, aby wyświetlić stronę ze szczegółami lub dodać do koszyka.
Normalne komponenty lub złożone komponenty - użyłem terminu złożony komponent, ponieważ zwykle są to komponenty na poziomie strony i składają się z wielu komponentów potomnych, a ponieważ każde dziecko może zachowywać się na swój własny, unikalny sposób, więc nie możesz być w 100% pewien, że renderują ten sam wynik dla danego stanu. Jak powiedziałem zwykle powinny być używane jako elementy kontenera
źródło
PureComponent
w komponentach na poziomie root i komponentach w górnej części hierarchii jest zwykle miejscem, w którym można zaobserwować największy wzrost wydajności. Oczywiście musisz unikać mutowania rekwizytów i informować bezpośrednio, aby czyste komponenty działały poprawnie, ale bezpośrednie mutowanie obiektów w każdym razie jest anty-wzorem w React.React.Component
jest domyślnym „normalnym” komponentem. Deklarujesz je za pomocąclass
słowa kluczowego iextends React.Component
. Pomyśl o nich jak o klasie z metodami cyklu życia, procedurami obsługi zdarzeń i dowolnymi metodami.React.PureComponent
toReact.Component
implementujeshouldComponentUpdate()
funkcję, która dokonuje płytkiego porównania jejprops
istate
. Musisz użyć,forceUpdate()
jeśli wiesz, że komponent ma rekwizyty lub zagnieżdżone dane, które uległy zmianie i chcesz ponownie renderować. Nie są więc świetne, jeśli potrzebujesz komponentów do ponownego renderowania, gdy tablice lub obiekty przekazywane jako rekwizyty lub ustawione w stanie zmieniają się.Komponenty funkcjonalne to takie, które nie mają funkcji cyklu życia. Są podobno bezpaństwowce, ale są tak ładne i czyste, że teraz mamy haczyki (od React 16.8), dzięki czemu możesz nadal mieć stan. Sądzę więc, że są to po prostu „czyste komponenty”.
źródło